diff --git a/crates/nu-cli/src/commands/to_csv.rs b/crates/nu-cli/src/commands/to_csv.rs index 680631326..da82fd0b7 100644 --- a/crates/nu-cli/src/commands/to_csv.rs +++ b/crates/nu-cli/src/commands/to_csv.rs @@ -2,7 +2,7 @@ use crate::commands::to_delimited_data::to_delimited_data; use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{Primitive, Signature, UntaggedValue, Value}; +use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value}; pub struct ToCSV; @@ -18,11 +18,18 @@ impl WholeStreamCommand for ToCSV { } fn signature(&self) -> Signature { - Signature::build("to-csv").switch( - "headerless", - "do not output the columns names as the first row", - None, - ) + Signature::build("to-csv") + .named( + "separator", + SyntaxShape::String, + "a character to separate columns, defaults to ','", + Some('s'), + ) + .switch( + "headerless", + "do not output the columns names as the first row", + None, + ) } fn usage(&self) -> &str { diff --git a/crates/nu-cli/src/commands/to_delimited_data.rs b/crates/nu-cli/src/commands/to_delimited_data.rs index 686a41b45..f8b8be6de 100644 --- a/crates/nu-cli/src/commands/to_delimited_data.rs +++ b/crates/nu-cli/src/commands/to_delimited_data.rs @@ -175,40 +175,41 @@ pub fn to_delimited_data( let name_span = name_tag.span; let stream = async_stream! { - let input: Vec = input.collect().await; + let input: Vec = input.collect().await; - let to_process_input = if input.len() > 1 { - let tag = input[0].tag.clone(); - vec![Value { value: UntaggedValue::Table(input), tag } ] - } else if input.len() == 1 { - input - } else { - vec![] - }; + let to_process_input = if input.len() > 1 { + let tag = input[0].tag.clone(); + vec![Value { value: UntaggedValue::Table(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; - for value in to_process_input { - match from_value_to_delimited_string(&clone_tagged_value(&value), sep) { - Ok(x) => { - let converted = if headerless { - x.lines().skip(1).collect() - } else { - x - }; - yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(converted)).into_value(&name_tag)) - } - Err(x) => { - let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name); - let requires = format!("requires {}-compatible input", format_name); - yield Err(ShellError::labeled_error_with_secondary( - expected, - requires, - name_span, - "originates from here".to_string(), - value.tag.span, - )) - } - } - } + for value in to_process_input { + match from_value_to_delimited_string(&clone_tagged_value(&value), sep) { + Ok(mut x) => { + if headerless { + x.find('\n').map(|second_line|{ + let start = second_line + 1; + x.replace_range(0..start, ""); + }); + } + yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag)) + } + Err(x) => { + let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name); + let requires = format!("requires {}-compatible input", format_name); + yield Err(ShellError::labeled_error_with_secondary( + expected, + requires, + name_span, + "originates from here".to_string(), + value.tag.span, + )) + } + } + } }; Ok(stream.to_output_stream())