mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Attempt to guess the content type of a file when opening with --raw (#13521)
# Description Attempt to guess the content type of a file when opening with --raw and set it in the pipeline metadata. <img width="644" alt="Screenshot 2024-08-02 at 11 30 10" src="https://github.com/user-attachments/assets/071f0967-c4dd-405a-b8c8-f7aa073efa98"> # User-Facing Changes - Content of files can be directly piped into commands like `http post` with the content type set appropriately when using `--raw`.
This commit is contained in:
parent
4e83ccdf86
commit
73e8de9753
5 changed files with 70 additions and 6 deletions
|
@ -146,11 +146,19 @@ impl Command for Open {
|
|||
}
|
||||
};
|
||||
|
||||
let content_type = if raw {
|
||||
path.extension()
|
||||
.map(|ext| ext.to_string_lossy().to_string())
|
||||
.and_then(|ref s| detect_content_type(s))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let stream = PipelineData::ByteStream(
|
||||
ByteStream::file(file, call_span, engine_state.signals().clone()),
|
||||
Some(PipelineMetadata {
|
||||
data_source: DataSource::FilePath(path.to_path_buf()),
|
||||
content_type: None,
|
||||
content_type,
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -268,3 +276,22 @@ fn extract_extensions(filename: &str) -> Vec<String> {
|
|||
|
||||
extensions
|
||||
}
|
||||
|
||||
fn detect_content_type(extension: &str) -> Option<String> {
|
||||
// This will allow the overriding of metadata to be consistent with
|
||||
// the content type
|
||||
match extension {
|
||||
// Per RFC-9512, application/yaml should be used
|
||||
"yaml" | "yml" => Some("application/yaml".to_string()),
|
||||
_ => mime_guess::from_ext(extension)
|
||||
.first()
|
||||
.map(|mime| mime.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn test_content_type() {}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ast::PathMember;
|
||||
use nu_protocol::{ast::PathMember, PipelineMetadata};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToToml;
|
||||
|
@ -100,9 +100,18 @@ fn toml_into_pipeline_data(
|
|||
toml_value: &toml::Value,
|
||||
value_type: Type,
|
||||
span: Span,
|
||||
metadata: Option<PipelineMetadata>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let new_md = Some(
|
||||
metadata
|
||||
.unwrap_or_default()
|
||||
.with_content_type(Some("text/x-toml".into())),
|
||||
);
|
||||
|
||||
match toml::to_string_pretty(&toml_value) {
|
||||
Ok(serde_toml_string) => Ok(Value::string(serde_toml_string, span).into_pipeline_data()),
|
||||
Ok(serde_toml_string) => {
|
||||
Ok(Value::string(serde_toml_string, span).into_pipeline_data_with_metadata(new_md))
|
||||
}
|
||||
_ => Ok(Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "TOML".into(),
|
||||
|
@ -112,7 +121,7 @@ fn toml_into_pipeline_data(
|
|||
},
|
||||
span,
|
||||
)
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data_with_metadata(new_md)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,6 +148,7 @@ fn to_toml(
|
|||
input: PipelineData,
|
||||
span: Span,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let metadata = input.metadata();
|
||||
let value = input.into_value(span)?;
|
||||
|
||||
let toml_value = value_to_toml_value(engine_state, &value, span)?;
|
||||
|
@ -148,10 +158,11 @@ fn to_toml(
|
|||
vec.iter().next().expect("this should never trigger"),
|
||||
value.get_type(),
|
||||
span,
|
||||
metadata,
|
||||
),
|
||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span),
|
||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span, metadata),
|
||||
},
|
||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span),
|
||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span, metadata),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
|||
let metadata = input
|
||||
.metadata()
|
||||
.unwrap_or_default()
|
||||
// Per RFC-9512, application/yaml should be used
|
||||
.with_content_type(Some("application/yaml".into()));
|
||||
let value = input.into_value(head)?;
|
||||
|
||||
|
|
|
@ -379,3 +379,27 @@ fn open_files_inside_glob_metachars_dir() {
|
|||
assert!(actual.out.contains("hello"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_content_types_with_open_raw() {
|
||||
Playground::setup("open_files_content_type_test", |dirs, _| {
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw random_numbers.csv | metadata");
|
||||
assert!(result.out.contains("text/csv"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw caco3_plastics.tsv | metadata");
|
||||
assert!(result.out.contains("text/tab-separated-values"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw sample-simple.json | metadata");
|
||||
assert!(result.out.contains("application/json"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw sample.ini | metadata");
|
||||
assert!(result.out.contains("text/plain"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw sample_data.xlsx | metadata");
|
||||
assert!(result.out.contains("vnd.openxmlformats-officedocument"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw sample_def.nu | metadata");
|
||||
assert!(!result.out.contains("content_type"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw sample.eml | metadata");
|
||||
assert!(result.out.contains("message/rfc822"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw cargo_sample.toml | metadata");
|
||||
assert!(result.out.contains("text/x-toml"));
|
||||
let result = nu!(cwd: dirs.formats(), "open --raw appveyor.yml | metadata");
|
||||
assert!(result.out.contains("application/yaml"));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::process::ExitStatus;
|
|||
// Needs to be reexported for `nu!` macro
|
||||
pub use nu_path;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Outcome {
|
||||
pub out: String,
|
||||
pub err: String,
|
||||
|
|
Loading…
Reference in a new issue