add "-0" as short for --headerless in "from" commands (#3042)

* replace --headerless flags with --noheaders / -n

* Update from_csv.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
This commit is contained in:
Saeed Rasooli 2021-02-22 10:55:17 +03:30 committed by GitHub
parent b5ae024cc8
commit 42d18d2294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 61 additions and 56 deletions

View File

@ -8,7 +8,7 @@ pub struct FromCSV;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FromCSVArgs { pub struct FromCSVArgs {
headerless: bool, noheaders: bool,
separator: Option<Value>, separator: Option<Value>,
} }
@ -27,9 +27,9 @@ impl WholeStreamCommand for FromCSV {
Some('s'), Some('s'),
) )
.switch( .switch(
"headerless", "noheaders",
"don't treat the first row as column names", "don't treat the first row as column names",
None, Some('n'),
) )
} }
@ -50,7 +50,12 @@ impl WholeStreamCommand for FromCSV {
}, },
Example { Example {
description: "Convert comma-separated data to a table, ignoring headers", description: "Convert comma-separated data to a table, ignoring headers",
example: "open data.txt | from csv --headerless", example: "open data.txt | from csv --noheaders",
result: None,
},
Example {
description: "Convert comma-separated data to a table, ignoring headers",
example: "open data.txt | from csv -n",
result: None, result: None,
}, },
Example { Example {
@ -67,7 +72,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ( let (
FromCSVArgs { FromCSVArgs {
headerless, noheaders,
separator, separator,
}, },
input, input,
@ -95,7 +100,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
_ => ',', _ => ',',
}; };
from_delimited_data(headerless, sep, "CSV", input, name).await from_delimited_data(noheaders, sep, "CSV", input, name).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -5,18 +5,18 @@ use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
fn from_delimited_string_to_value( fn from_delimited_string_to_value(
s: String, s: String,
headerless: bool, noheaders: bool,
separator: char, separator: char,
tag: impl Into<Tag>, tag: impl Into<Tag>,
) -> Result<Value, csv::Error> { ) -> Result<Value, csv::Error> {
let mut reader = ReaderBuilder::new() let mut reader = ReaderBuilder::new()
.has_headers(!headerless) .has_headers(!noheaders)
.delimiter(separator as u8) .delimiter(separator as u8)
.from_reader(s.as_bytes()); .from_reader(s.as_bytes());
let tag = tag.into(); let tag = tag.into();
let span = tag.span; let span = tag.span;
let headers = if headerless { let headers = if noheaders {
(1..=reader.headers()?.len()) (1..=reader.headers()?.len())
.map(|i| format!("Column{}", i)) .map(|i| format!("Column{}", i))
.collect::<Vec<String>>() .collect::<Vec<String>>()
@ -46,7 +46,7 @@ fn from_delimited_string_to_value(
} }
pub async fn from_delimited_data( pub async fn from_delimited_data(
headerless: bool, noheaders: bool,
sep: char, sep: char,
format_name: &'static str, format_name: &'static str,
input: InputStream, input: InputStream,
@ -56,7 +56,7 @@ pub async fn from_delimited_data(
let concat_string = input.collect_string(name_tag.clone()).await?; let concat_string = input.collect_string(name_tag.clone()).await?;
let sample_lines = concat_string.item.lines().take(3).collect_vec().join("\n"); let sample_lines = concat_string.item.lines().take(3).collect_vec().join("\n");
match from_delimited_string_to_value(concat_string.item, headerless, sep, name_tag.clone()) { match from_delimited_string_to_value(concat_string.item, noheaders, sep, name_tag.clone()) {
Ok(x) => match x { Ok(x) => match x {
Value { Value {
value: UntaggedValue::Table(list), value: UntaggedValue::Table(list),

View File

@ -10,7 +10,7 @@ pub struct FromODS;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FromODSArgs { pub struct FromODSArgs {
headerless: bool, noheaders: bool,
} }
#[async_trait] #[async_trait]
@ -21,9 +21,9 @@ impl WholeStreamCommand for FromODS {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("from ods").switch( Signature::build("from ods").switch(
"headerless", "noheaders",
"don't treat the first row as column names", "don't treat the first row as column names",
None, Some('n'),
) )
} }
@ -42,7 +42,7 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ( let (
FromODSArgs { FromODSArgs {
headerless: _headerless, noheaders: _noheaders,
}, },
input, input,
) = args.process().await?; ) = args.process().await?;

View File

@ -10,7 +10,7 @@ pub struct FromSSV;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FromSSVArgs { pub struct FromSSVArgs {
headerless: bool, noheaders: bool,
#[serde(rename(deserialize = "aligned-columns"))] #[serde(rename(deserialize = "aligned-columns"))]
aligned_columns: bool, aligned_columns: bool,
#[serde(rename(deserialize = "minimum-spaces"))] #[serde(rename(deserialize = "minimum-spaces"))]
@ -29,9 +29,9 @@ impl WholeStreamCommand for FromSSV {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(STRING_REPRESENTATION) Signature::build(STRING_REPRESENTATION)
.switch( .switch(
"headerless", "noheaders",
"don't treat the first row as column names", "don't treat the first row as column names",
None, Some('n'),
) )
.switch("aligned-columns", "assume columns are aligned", Some('a')) .switch("aligned-columns", "assume columns are aligned", Some('a'))
.named( .named(
@ -196,14 +196,14 @@ fn parse_separated_columns<'a>(
fn string_to_table( fn string_to_table(
s: &str, s: &str,
headerless: bool, noheaders: bool,
aligned_columns: bool, aligned_columns: bool,
split_at: usize, split_at: usize,
) -> Vec<Vec<(String, String)>> { ) -> Vec<Vec<(String, String)>> {
let mut lines = s.lines().filter(|l| !l.trim().is_empty()); let mut lines = s.lines().filter(|l| !l.trim().is_empty());
let separator = " ".repeat(std::cmp::max(split_at, 1)); let separator = " ".repeat(std::cmp::max(split_at, 1));
let (ls, header_options) = if headerless { let (ls, header_options) = if noheaders {
(lines, HeaderOptions::WithoutHeaders) (lines, HeaderOptions::WithoutHeaders)
} else { } else {
match lines.next() { match lines.next() {
@ -223,13 +223,13 @@ fn string_to_table(
fn from_ssv_string_to_value( fn from_ssv_string_to_value(
s: &str, s: &str,
headerless: bool, noheaders: bool,
aligned_columns: bool, aligned_columns: bool,
split_at: usize, split_at: usize,
tag: impl Into<Tag>, tag: impl Into<Tag>,
) -> Value { ) -> Value {
let tag = tag.into(); let tag = tag.into();
let rows = string_to_table(s, headerless, aligned_columns, split_at) let rows = string_to_table(s, noheaders, aligned_columns, split_at)
.iter() .iter()
.map(|row| { .map(|row| {
let mut tagged_dict = TaggedDictBuilder::new(&tag); let mut tagged_dict = TaggedDictBuilder::new(&tag);
@ -251,7 +251,7 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let ( let (
FromSSVArgs { FromSSVArgs {
headerless, noheaders,
aligned_columns, aligned_columns,
minimum_spaces, minimum_spaces,
}, },
@ -266,7 +266,7 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok( Ok(
match from_ssv_string_to_value( match from_ssv_string_to_value(
&concat_string.item, &concat_string.item,
headerless, noheaders,
aligned_columns, aligned_columns,
split_at, split_at,
name.clone(), name.clone(),
@ -323,7 +323,7 @@ mod tests {
} }
#[test] #[test]
fn it_uses_first_row_as_data_when_headerless() { fn it_uses_first_row_as_data_when_noheaders() {
let input = r#" let input = r#"
a b a b
1 2 1 2
@ -435,7 +435,7 @@ mod tests {
} }
#[test] #[test]
fn it_handles_empty_values_when_headerless_and_aligned_columns() { fn it_handles_empty_values_when_noheaders_and_aligned_columns() {
let input = r#" let input = r#"
a multi-word value b d a multi-word value b d
1 3-3 4 1 3-3 4
@ -479,11 +479,11 @@ mod tests {
kubernetes-ro component=apiserver,provider=kubernetes <none> 172.30.0.1 80/TCP kubernetes-ro component=apiserver,provider=kubernetes <none> 172.30.0.1 80/TCP
"#; "#;
let aligned_columns_headerless = string_to_table(input, true, true, 2); let aligned_columns_noheaders = string_to_table(input, true, true, 2);
let separator_headerless = string_to_table(input, true, false, 2); let separator_noheaders = string_to_table(input, true, false, 2);
let aligned_columns_with_headers = string_to_table(input, false, true, 2); let aligned_columns_with_headers = string_to_table(input, false, true, 2);
let separator_with_headers = string_to_table(input, false, false, 2); let separator_with_headers = string_to_table(input, false, false, 2);
assert_eq!(aligned_columns_headerless, separator_headerless); assert_eq!(aligned_columns_noheaders, separator_noheaders);
assert_eq!(aligned_columns_with_headers, separator_with_headers); assert_eq!(aligned_columns_with_headers, separator_with_headers);
} }

View File

@ -8,7 +8,7 @@ pub struct FromTSV;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FromTSVArgs { pub struct FromTSVArgs {
headerless: bool, noheaders: bool,
} }
#[async_trait] #[async_trait]
@ -19,9 +19,9 @@ impl WholeStreamCommand for FromTSV {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("from tsv").switch( Signature::build("from tsv").switch(
"headerless", "noheaders",
"don't treat the first row as column names", "don't treat the first row as column names",
None, Some('n'),
) )
} }
@ -36,9 +36,9 @@ impl WholeStreamCommand for FromTSV {
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> { async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (FromTSVArgs { headerless }, input) = args.process().await?; let (FromTSVArgs { noheaders }, input) = args.process().await?;
from_delimited_data(headerless, '\t', "TSV", input, name).await from_delimited_data(noheaders, '\t', "TSV", input, name).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -10,7 +10,7 @@ pub struct FromXLSX;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FromXLSXArgs { pub struct FromXLSXArgs {
headerless: bool, noheaders: bool,
} }
#[async_trait] #[async_trait]
@ -21,9 +21,9 @@ impl WholeStreamCommand for FromXLSX {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("from xlsx").switch( Signature::build("from xlsx").switch(
"headerless", "noheaders",
"don't treat the first row as column names", "don't treat the first row as column names",
None, Some('n'),
) )
} }
@ -41,7 +41,7 @@ async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
let span = tag.span; let span = tag.span;
let ( let (
FromXLSXArgs { FromXLSXArgs {
headerless: _headerless, noheaders: _noheaders,
}, },
input, input,
) = args.process().await?; ) = args.process().await?;

View File

@ -8,7 +8,7 @@ pub struct ToCSV;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct ToCSVArgs { pub struct ToCSVArgs {
headerless: bool, noheaders: bool,
separator: Option<Value>, separator: Option<Value>,
} }
@ -27,9 +27,9 @@ impl WholeStreamCommand for ToCSV {
Some('s'), Some('s'),
) )
.switch( .switch(
"headerless", "noheaders",
"do not output the columns names as the first row", "do not output the columns names as the first row",
None, Some('n'),
) )
} }
@ -47,7 +47,7 @@ async fn to_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ( let (
ToCSVArgs { ToCSVArgs {
separator, separator,
headerless, noheaders,
}, },
input, input,
) = args.process().await?; ) = args.process().await?;
@ -74,7 +74,7 @@ async fn to_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
_ => ',', _ => ',',
}; };
to_delimited_data(headerless, sep, "CSV", input, name).await to_delimited_data(noheaders, sep, "CSV", input, name).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -165,7 +165,7 @@ fn merge_descriptors(values: &[Value]) -> Vec<Spanned<String>> {
} }
pub async fn to_delimited_data( pub async fn to_delimited_data(
headerless: bool, noheaders: bool,
sep: char, sep: char,
format_name: &'static str, format_name: &'static str,
input: InputStream, input: InputStream,
@ -192,7 +192,7 @@ pub async fn to_delimited_data(
futures::stream::iter(to_process_input.into_iter().map(move |value| { futures::stream::iter(to_process_input.into_iter().map(move |value| {
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) { match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
Ok(mut x) => { Ok(mut x) => {
if headerless { if noheaders {
if let Some(second_line) = x.find('\n') { if let Some(second_line) = x.find('\n') {
let start = second_line + 1; let start = second_line + 1;
x.replace_range(0..start, ""); x.replace_range(0..start, "");

View File

@ -8,7 +8,7 @@ pub struct ToTSV;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct ToTSVArgs { pub struct ToTSVArgs {
headerless: bool, noheaders: bool,
} }
#[async_trait] #[async_trait]
@ -19,9 +19,9 @@ impl WholeStreamCommand for ToTSV {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("to tsv").switch( Signature::build("to tsv").switch(
"headerless", "noheaders",
"do not output the column names as the first row", "do not output the column names as the first row",
None, Some('n'),
) )
} }
@ -36,9 +36,9 @@ impl WholeStreamCommand for ToTSV {
async fn to_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> { async fn to_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (ToTSVArgs { headerless }, input) = args.process().await?; let (ToTSVArgs { noheaders }, input) = args.process().await?;
to_delimited_data(headerless, '\t', "TSV", input, name).await to_delimited_data(noheaders, '\t', "TSV", input, name).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -64,7 +64,7 @@ fn table_to_csv_text_skipping_headers_after_conversion() {
| str trim | str trim
| split column "," a b c d origin | split column "," a b c d origin
| last 1 | last 1
| to csv --headerless | to csv --noheaders
"# "#
)); ));
@ -198,7 +198,7 @@ fn from_csv_text_skipping_headers_to_table() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
open los_tres_amigos.txt open los_tres_amigos.txt
| from csv --headerless | from csv --noheaders
| get Column3 | get Column3
| count | count
"# "#

View File

@ -72,7 +72,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
open oc_get_svc.txt open oc_get_svc.txt
| from ssv --headerless -a | from ssv --noheaders -a
| first | first
| get Column1 | get Column1
"# "#
@ -82,7 +82,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
open oc_get_svc.txt open oc_get_svc.txt
| from ssv --headerless | from ssv --noheaders
| first | first
| get Column1 | get Column1

View File

@ -70,7 +70,7 @@ fn table_to_tsv_text_skipping_headers_after_conversion() {
| lines | lines
| split column "\t" a b c d origin | split column "\t" a b c d origin
| last 1 | last 1
| to tsv --headerless | to tsv --noheaders
"# "#
)); ));
@ -121,7 +121,7 @@ fn from_tsv_text_skipping_headers_to_table() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
open los_tres_amigos.txt open los_tres_amigos.txt
| from tsv --headerless | from tsv --noheaders
| get Column3 | get Column3
| count | count
"# "#