Add metadata on open --raw with bytestreams (#14141)

# Description

This PR closes #14137 and allows the display hook to be set on byte
streams. So, with a hook like this below.
```nushell
display_output: {
    metadata access {|meta| match $meta.content_type? {
        "application/x-nuscript" | "application/x-nuon" | "text/x-nushell" => { nu-highlight },
        "application/json" => { ^bat --language=json --color=always --style=plain --paging=never },
        _ => {},
        }
    } | table
}
```
You could type `open toolkit.nu` and the text of toolkit.nu would be
highlighted by nu-highlight. This PR also changes the way content-type
is assigned with `open`. Previously it would only assign it if `--raw`
was specified.

Lastly, it changes the `is_external()` function to only say
`ByteStreamSource::Child`'s are external instead of both Child and
`ByteStreamSource::File`. Again, this was to allow the hook to function
properly. I'm not sure what negative ramifications changing
`is_external()` could have, but there may be some?

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
This commit is contained in:
Darren Schroeder 2024-10-23 16:50:15 -05:00 committed by GitHub
parent abb6fca5e3
commit af9c31152a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 27 additions and 13 deletions

View File

@ -282,8 +282,22 @@ fn evaluate_source(
}?; }?;
if let PipelineData::ByteStream(..) = pipeline { if let PipelineData::ByteStream(..) = pipeline {
pipeline.print(engine_state, stack, false, false) // run the display hook on bytestreams too
} else if let Some(hook) = engine_state.get_config().hooks.display_output.clone() { run_display_hook(engine_state, stack, pipeline, false)
} else {
run_display_hook(engine_state, stack, pipeline, true)
}?;
Ok(false)
}
fn run_display_hook(
engine_state: &mut EngineState,
stack: &mut Stack,
pipeline: PipelineData,
no_newline: bool,
) -> Result<(), ShellError> {
if let Some(hook) = engine_state.get_config().hooks.display_output.clone() {
let pipeline = eval_hook( let pipeline = eval_hook(
engine_state, engine_state,
stack, stack,
@ -292,14 +306,11 @@ fn evaluate_source(
&hook, &hook,
"display_output", "display_output",
)?; )?;
pipeline.print(engine_state, stack, false, false) pipeline.print(engine_state, stack, no_newline, false)
} else { } else {
pipeline.print(engine_state, stack, true, false) pipeline.print(engine_state, stack, no_newline, false)
}?; }
Ok(false)
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -146,6 +146,9 @@ impl Command for Open {
} }
}; };
// Assigning content type should only happen in raw mode. Otherwise, the content
// will potentially be in one of the built-in nushell `from xxx` formats and therefore
// cease to be in the original content-type.... or so I'm told. :)
let content_type = if raw { let content_type = if raw {
path.extension() path.extension()
.map(|ext| ext.to_string_lossy().to_string()) .map(|ext| ext.to_string_lossy().to_string())
@ -283,6 +286,9 @@ fn detect_content_type(extension: &str) -> Option<String> {
match extension { match extension {
// Per RFC-9512, application/yaml should be used // Per RFC-9512, application/yaml should be used
"yaml" | "yml" => Some("application/yaml".to_string()), "yaml" | "yml" => Some("application/yaml".to_string()),
"nu" => Some("application/x-nuscript".to_string()),
"json" | "jsonl" | "ndjson" => Some("application/json".to_string()),
"nuon" => Some("application/x-nuon".to_string()),
_ => mime_guess::from_ext(extension) _ => mime_guess::from_ext(extension)
.first() .first()
.map(|mime| mime.to_string()), .map(|mime| mime.to_string()),

View File

@ -394,7 +394,7 @@ fn test_content_types_with_open_raw() {
let result = nu!(cwd: dirs.formats(), "open --raw sample_data.xlsx | metadata"); let result = nu!(cwd: dirs.formats(), "open --raw sample_data.xlsx | metadata");
assert!(result.out.contains("vnd.openxmlformats-officedocument")); assert!(result.out.contains("vnd.openxmlformats-officedocument"));
let result = nu!(cwd: dirs.formats(), "open --raw sample_def.nu | metadata"); let result = nu!(cwd: dirs.formats(), "open --raw sample_def.nu | metadata");
assert!(!result.out.contains("content_type")); assert!(result.out.contains("application/x-nuscript"));
let result = nu!(cwd: dirs.formats(), "open --raw sample.eml | metadata"); let result = nu!(cwd: dirs.formats(), "open --raw sample.eml | metadata");
assert!(result.out.contains("message/rfc822")); assert!(result.out.contains("message/rfc822"));
let result = nu!(cwd: dirs.formats(), "open --raw cargo_sample.toml | metadata"); let result = nu!(cwd: dirs.formats(), "open --raw cargo_sample.toml | metadata");

View File

@ -41,10 +41,7 @@ impl ByteStreamSource {
/// Source is a `Child` or `File`, rather than `Read`. Currently affects trimming /// Source is a `Child` or `File`, rather than `Read`. Currently affects trimming
fn is_external(&self) -> bool { fn is_external(&self) -> bool {
matches!( matches!(self, ByteStreamSource::Child(..))
self,
ByteStreamSource::File(..) | ByteStreamSource::Child(..)
)
} }
} }