mirror of
https://github.com/nushell/nushell
synced 2024-12-28 14:03:09 +00:00
setting content type metadata on all core to *
commands (#13506)
# Description All core `to *` set content type pipeline metadata. # User-Facing Changes - For consistency, `from json` no longer sets the content type metadata # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib
This commit is contained in:
parent
2c6b1471e1
commit
d34a24db33
12 changed files with 411 additions and 37 deletions
|
@ -72,7 +72,7 @@ pub fn check_example_input_and_output_types_match_command_signature(
|
||||||
witnessed_type_transformations
|
witnessed_type_transformations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_pipeline_without_terminal_expression(
|
pub fn eval_pipeline_without_terminal_expression(
|
||||||
src: &str,
|
src: &str,
|
||||||
cwd: &std::path::Path,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io::{BufRead, Cursor};
|
use std::io::{BufRead, Cursor};
|
||||||
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{ListStream, PipelineMetadata, Signals};
|
use nu_protocol::{ListStream, Signals};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FromJson;
|
pub struct FromJson;
|
||||||
|
@ -83,7 +83,7 @@ impl Command for FromJson {
|
||||||
strict,
|
strict,
|
||||||
engine_state.signals().clone(),
|
engine_state.signals().clone(),
|
||||||
),
|
),
|
||||||
update_metadata(metadata),
|
metadata,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
PipelineData::ByteStream(stream, metadata)
|
PipelineData::ByteStream(stream, metadata)
|
||||||
|
@ -92,7 +92,7 @@ impl Command for FromJson {
|
||||||
if let Some(reader) = stream.reader() {
|
if let Some(reader) = stream.reader() {
|
||||||
Ok(PipelineData::ListStream(
|
Ok(PipelineData::ListStream(
|
||||||
read_json_lines(reader, span, strict, Signals::empty()),
|
read_json_lines(reader, span, strict, Signals::empty()),
|
||||||
update_metadata(metadata),
|
metadata,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(PipelineData::Empty)
|
Ok(PipelineData::Empty)
|
||||||
|
@ -115,10 +115,10 @@ impl Command for FromJson {
|
||||||
|
|
||||||
if strict {
|
if strict {
|
||||||
Ok(convert_string_to_value_strict(&string_input, span)?
|
Ok(convert_string_to_value_strict(&string_input, span)?
|
||||||
.into_pipeline_data_with_metadata(update_metadata(metadata)))
|
.into_pipeline_data_with_metadata(metadata))
|
||||||
} else {
|
} else {
|
||||||
Ok(convert_string_to_value(&string_input, span)?
|
Ok(convert_string_to_value(&string_input, span)?
|
||||||
.into_pipeline_data_with_metadata(update_metadata(metadata)))
|
.into_pipeline_data_with_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,14 +265,6 @@ fn convert_string_to_value_strict(string_input: &str, span: Span) -> Result<Valu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
|
||||||
metadata
|
|
||||||
.map(|md| md.with_content_type(Some("application/json".into())))
|
|
||||||
.or_else(|| {
|
|
||||||
Some(PipelineMetadata::default().with_content_type(Some("application/json".into())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -4,6 +4,8 @@ use crate::formats::to::delimited::to_delimited_data;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Config;
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
use super::delimited::ToDelimitedDataArgs;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToCsv;
|
pub struct ToCsv;
|
||||||
|
|
||||||
|
@ -116,17 +118,62 @@ fn to_csv(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
to_delimited_data(noheaders, sep, columns, "CSV", input, head, config)
|
to_delimited_data(
|
||||||
|
ToDelimitedDataArgs {
|
||||||
|
noheaders,
|
||||||
|
separator: sep,
|
||||||
|
columns,
|
||||||
|
format_name: "CSV",
|
||||||
|
input,
|
||||||
|
head,
|
||||||
|
content_type: Some(mime::TEXT_CSV.to_string()),
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
use crate::test_examples;
|
use crate::test_examples;
|
||||||
|
|
||||||
test_examples(ToCsv {})
|
test_examples(ToCsv {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToCsv {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/csv"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,18 +69,36 @@ fn make_cant_convert_error(value: &Value, format_name: &'static str) -> ShellErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ToDelimitedDataArgs {
|
||||||
|
pub noheaders: bool,
|
||||||
|
pub separator: Spanned<char>,
|
||||||
|
pub columns: Option<Vec<String>>,
|
||||||
|
pub format_name: &'static str,
|
||||||
|
pub input: PipelineData,
|
||||||
|
pub head: Span,
|
||||||
|
pub content_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_delimited_data(
|
pub fn to_delimited_data(
|
||||||
noheaders: bool,
|
ToDelimitedDataArgs {
|
||||||
separator: Spanned<char>,
|
noheaders,
|
||||||
columns: Option<Vec<String>>,
|
separator,
|
||||||
format_name: &'static str,
|
columns,
|
||||||
input: PipelineData,
|
format_name,
|
||||||
head: Span,
|
input,
|
||||||
|
head,
|
||||||
|
content_type,
|
||||||
|
}: ToDelimitedDataArgs,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut input = input;
|
let mut input = input;
|
||||||
let span = input.span().unwrap_or(head);
|
let span = input.span().unwrap_or(head);
|
||||||
let metadata = input.metadata();
|
let metadata = Some(
|
||||||
|
input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(content_type),
|
||||||
|
);
|
||||||
|
|
||||||
let separator = u8::try_from(separator.item).map_err(|_| ShellError::IncorrectValue {
|
let separator = u8::try_from(separator.item).map_err(|_| ShellError::IncorrectValue {
|
||||||
msg: "separator must be an ASCII character".into(),
|
msg: "separator must be an ASCII character".into(),
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl Command for ToJson {
|
||||||
let res = Value::string(serde_json_string, span);
|
let res = Value::string(serde_json_string, span);
|
||||||
let metadata = PipelineMetadata {
|
let metadata = PipelineMetadata {
|
||||||
data_source: nu_protocol::DataSource::None,
|
data_source: nu_protocol::DataSource::None,
|
||||||
content_type: Some("application/json".to_string()),
|
content_type: Some(mime::APPLICATION_JSON.to_string()),
|
||||||
};
|
};
|
||||||
Ok(PipelineData::Value(res, Some(metadata)))
|
Ok(PipelineData::Value(res, Some(metadata)))
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,10 @@ fn json_list(input: &[Value]) -> Result<Vec<nu_json::Value>, ShellError> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -167,4 +171,34 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToJson {})
|
test_examples(ToJson {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToJson {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/json"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,12 @@ fn to_md(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// text/markdown became a valid mimetype with rfc7763
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("text/markdown".into()));
|
||||||
|
|
||||||
let (grouped_input, single_list) = group_by(input, head, config);
|
let (grouped_input, single_list) = group_by(input, head, config);
|
||||||
if per_element || single_list {
|
if per_element || single_list {
|
||||||
return Ok(Value::string(
|
return Ok(Value::string(
|
||||||
|
@ -95,9 +101,10 @@ fn to_md(
|
||||||
.join(""),
|
.join(""),
|
||||||
head,
|
head,
|
||||||
)
|
)
|
||||||
.into_pipeline_data());
|
.into_pipeline_data_with_metadata(Some(metadata)));
|
||||||
}
|
}
|
||||||
Ok(Value::string(table(grouped_input, pretty, config), head).into_pipeline_data())
|
Ok(Value::string(table(grouped_input, pretty, config), head)
|
||||||
|
.into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
||||||
|
@ -328,7 +335,10 @@ fn get_padded_string(text: String, desired_length: usize, padding_character: cha
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
use nu_protocol::{record, Config, IntoPipelineData, Value};
|
use nu_protocol::{record, Config, IntoPipelineData, Value};
|
||||||
|
|
||||||
fn one(string: &str) -> String {
|
fn one(string: &str) -> String {
|
||||||
|
@ -453,4 +463,35 @@ mod tests {
|
||||||
"#)
|
"#)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let state_delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToMd {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
let delta = state_delta;
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/markdown"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,13 +74,18 @@ MessagePack: https://msgpack.org/
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/x-msgpack".into()));
|
||||||
|
|
||||||
let value_span = input.span().unwrap_or(call.head);
|
let value_span = input.span().unwrap_or(call.head);
|
||||||
let value = input.into_value(value_span)?;
|
let value = input.into_value(value_span)?;
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
write_value(&mut out, &value, 0)?;
|
write_value(&mut out, &value, 0)?;
|
||||||
|
|
||||||
Ok(Value::binary(out, call.head).into_pipeline_data())
|
Ok(Value::binary(out, call.head).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +273,10 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -276,4 +285,36 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToMsgpack {})
|
test_examples(ToMsgpack {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToMsgpack {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to msgpack | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(
|
||||||
|
record!("content_type" => Value::test_string("application/x-msgpack"))
|
||||||
|
),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,11 @@ impl Command for ToNuon {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/x-nuon".into()));
|
||||||
|
|
||||||
let style = if call.has_flag(engine_state, stack, "raw")? {
|
let style = if call.has_flag(engine_state, stack, "raw")? {
|
||||||
nuon::ToStyle::Raw
|
nuon::ToStyle::Raw
|
||||||
} else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
|
} else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
|
||||||
|
@ -56,9 +61,8 @@ impl Command for ToNuon {
|
||||||
let value = input.into_value(span)?;
|
let value = input.into_value(span)?;
|
||||||
|
|
||||||
match nuon::to_nuon(&value, style, Some(span)) {
|
match nuon::to_nuon(&value, style, Some(span)) {
|
||||||
Ok(serde_nuon_string) => {
|
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
|
||||||
Ok(Value::string(serde_nuon_string, span).into_pipeline_data())
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
|
||||||
_ => Ok(Value::error(
|
_ => Ok(Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "NUON".into(),
|
to_type: "NUON".into(),
|
||||||
|
@ -68,7 +72,7 @@ impl Command for ToNuon {
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
.into_pipeline_data()),
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,10 +104,45 @@ impl Command for ToNuon {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
use super::ToNuon;
|
use super::ToNuon;
|
||||||
use crate::test_examples;
|
use crate::test_examples;
|
||||||
test_examples(ToNuon {})
|
test_examples(ToNuon {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToNuon {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to nuon | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/x-nuon"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,14 +134,18 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||||
|
|
||||||
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
||||||
metadata
|
metadata
|
||||||
.map(|md| md.with_content_type(Some("text/plain".to_string())))
|
.map(|md| md.with_content_type(Some(mime::TEXT_PLAIN.to_string())))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
Some(PipelineMetadata::default().with_content_type(Some("text/plain".to_string())))
|
Some(PipelineMetadata::default().with_content_type(Some(mime::TEXT_PLAIN.to_string())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -150,4 +154,34 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToText {})
|
test_examples(ToText {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToText {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to text | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/plain"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ use crate::formats::to::delimited::to_delimited_data;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Config;
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
use super::delimited::ToDelimitedDataArgs;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToTsv;
|
pub struct ToTsv;
|
||||||
|
|
||||||
|
@ -82,11 +84,26 @@ fn to_tsv(
|
||||||
item: '\t',
|
item: '\t',
|
||||||
span: head,
|
span: head,
|
||||||
};
|
};
|
||||||
to_delimited_data(noheaders, sep, columns, "TSV", input, head, config)
|
to_delimited_data(
|
||||||
|
ToDelimitedDataArgs {
|
||||||
|
noheaders,
|
||||||
|
separator: sep,
|
||||||
|
columns,
|
||||||
|
format_name: "TSV",
|
||||||
|
input,
|
||||||
|
head,
|
||||||
|
content_type: Some(mime::TEXT_TAB_SEPARATED_VALUES.to_string()),
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -95,4 +112,36 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToTsv {})
|
test_examples(ToTsv {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToTsv {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to tsv | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(
|
||||||
|
record!("content_type" => Value::test_string("text/tab-separated-values"))
|
||||||
|
),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,10 @@ impl Job {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self, input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn run(mut self, input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/xml".into()));
|
||||||
let value = input.into_value(head)?;
|
let value = input.into_value(head)?;
|
||||||
|
|
||||||
self.write_xml_entry(value, true).and_then(|_| {
|
self.write_xml_entry(value, true).and_then(|_| {
|
||||||
|
@ -141,7 +145,7 @@ impl Job {
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::NonUtf8 { span: head });
|
return Err(ShellError::NonUtf8 { span: head });
|
||||||
};
|
};
|
||||||
Ok(Value::string(s, head).into_pipeline_data())
|
Ok(Value::string(s, head).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,6 +512,10 @@ impl Job {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -516,4 +524,34 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToXml {})
|
test_examples(ToXml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToXml {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attributes: null content : Event}]}]} | to xml | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/xml"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,11 +95,18 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/yaml".into()));
|
||||||
let value = input.into_value(head)?;
|
let value = input.into_value(head)?;
|
||||||
|
|
||||||
let yaml_value = value_to_yaml_value(&value)?;
|
let yaml_value = value_to_yaml_value(&value)?;
|
||||||
match serde_yaml::to_string(&yaml_value) {
|
match serde_yaml::to_string(&yaml_value) {
|
||||||
Ok(serde_yaml_string) => Ok(Value::string(serde_yaml_string, head).into_pipeline_data()),
|
Ok(serde_yaml_string) => {
|
||||||
|
Ok(Value::string(serde_yaml_string, head)
|
||||||
|
.into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
|
}
|
||||||
_ => Ok(Value::error(
|
_ => Ok(Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "YAML".into(),
|
to_type: "YAML".into(),
|
||||||
|
@ -109,12 +116,16 @@ fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
||||||
},
|
},
|
||||||
head,
|
head,
|
||||||
)
|
)
|
||||||
.into_pipeline_data()),
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -123,4 +134,34 @@ mod test {
|
||||||
|
|
||||||
test_examples(ToYaml {})
|
test_examples(ToYaml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToYaml {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to yaml | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/yaml"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue