forked from extern/nushell
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:
parent
b5ae024cc8
commit
42d18d2294
@ -8,7 +8,7 @@ pub struct FromCSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromCSVArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
separator: Option<Value>,
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ impl WholeStreamCommand for FromCSV {
|
||||
Some('s'),
|
||||
)
|
||||
.switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"don't treat the first row as column names",
|
||||
None,
|
||||
Some('n'),
|
||||
)
|
||||
}
|
||||
|
||||
@ -50,7 +50,12 @@ impl WholeStreamCommand for FromCSV {
|
||||
},
|
||||
Example {
|
||||
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,
|
||||
},
|
||||
Example {
|
||||
@ -67,7 +72,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let (
|
||||
FromCSVArgs {
|
||||
headerless,
|
||||
noheaders,
|
||||
separator,
|
||||
},
|
||||
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)]
|
||||
|
@ -5,18 +5,18 @@ use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
fn from_delimited_string_to_value(
|
||||
s: String,
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
separator: char,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, csv::Error> {
|
||||
let mut reader = ReaderBuilder::new()
|
||||
.has_headers(!headerless)
|
||||
.has_headers(!noheaders)
|
||||
.delimiter(separator as u8)
|
||||
.from_reader(s.as_bytes());
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
let headers = if headerless {
|
||||
let headers = if noheaders {
|
||||
(1..=reader.headers()?.len())
|
||||
.map(|i| format!("Column{}", i))
|
||||
.collect::<Vec<String>>()
|
||||
@ -46,7 +46,7 @@ fn from_delimited_string_to_value(
|
||||
}
|
||||
|
||||
pub async fn from_delimited_data(
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
sep: char,
|
||||
format_name: &'static str,
|
||||
input: InputStream,
|
||||
@ -56,7 +56,7 @@ pub async fn from_delimited_data(
|
||||
let concat_string = input.collect_string(name_tag.clone()).await?;
|
||||
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 {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
|
@ -10,7 +10,7 @@ pub struct FromODS;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromODSArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -21,9 +21,9 @@ impl WholeStreamCommand for FromODS {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from ods").switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"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 (
|
||||
FromODSArgs {
|
||||
headerless: _headerless,
|
||||
noheaders: _noheaders,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
|
@ -10,7 +10,7 @@ pub struct FromSSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromSSVArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
#[serde(rename(deserialize = "aligned-columns"))]
|
||||
aligned_columns: bool,
|
||||
#[serde(rename(deserialize = "minimum-spaces"))]
|
||||
@ -29,9 +29,9 @@ impl WholeStreamCommand for FromSSV {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(STRING_REPRESENTATION)
|
||||
.switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"don't treat the first row as column names",
|
||||
None,
|
||||
Some('n'),
|
||||
)
|
||||
.switch("aligned-columns", "assume columns are aligned", Some('a'))
|
||||
.named(
|
||||
@ -196,14 +196,14 @@ fn parse_separated_columns<'a>(
|
||||
|
||||
fn string_to_table(
|
||||
s: &str,
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
aligned_columns: bool,
|
||||
split_at: usize,
|
||||
) -> Vec<Vec<(String, String)>> {
|
||||
let mut lines = s.lines().filter(|l| !l.trim().is_empty());
|
||||
let separator = " ".repeat(std::cmp::max(split_at, 1));
|
||||
|
||||
let (ls, header_options) = if headerless {
|
||||
let (ls, header_options) = if noheaders {
|
||||
(lines, HeaderOptions::WithoutHeaders)
|
||||
} else {
|
||||
match lines.next() {
|
||||
@ -223,13 +223,13 @@ fn string_to_table(
|
||||
|
||||
fn from_ssv_string_to_value(
|
||||
s: &str,
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
aligned_columns: bool,
|
||||
split_at: usize,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Value {
|
||||
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()
|
||||
.map(|row| {
|
||||
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 (
|
||||
FromSSVArgs {
|
||||
headerless,
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
minimum_spaces,
|
||||
},
|
||||
@ -266,7 +266,7 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(
|
||||
match from_ssv_string_to_value(
|
||||
&concat_string.item,
|
||||
headerless,
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
split_at,
|
||||
name.clone(),
|
||||
@ -323,7 +323,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_uses_first_row_as_data_when_headerless() {
|
||||
fn it_uses_first_row_as_data_when_noheaders() {
|
||||
let input = r#"
|
||||
a b
|
||||
1 2
|
||||
@ -435,7 +435,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_handles_empty_values_when_headerless_and_aligned_columns() {
|
||||
fn it_handles_empty_values_when_noheaders_and_aligned_columns() {
|
||||
let input = r#"
|
||||
a multi-word value b d
|
||||
1 3-3 4
|
||||
@ -479,11 +479,11 @@ mod tests {
|
||||
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 separator_headerless = string_to_table(input, true, false, 2);
|
||||
let aligned_columns_noheaders = string_to_table(input, true, true, 2);
|
||||
let separator_noheaders = string_to_table(input, true, false, 2);
|
||||
let aligned_columns_with_headers = string_to_table(input, false, true, 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);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ pub struct FromTSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromTSVArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -19,9 +19,9 @@ impl WholeStreamCommand for FromTSV {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from tsv").switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"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> {
|
||||
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)]
|
||||
|
@ -10,7 +10,7 @@ pub struct FromXLSX;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromXLSXArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -21,9 +21,9 @@ impl WholeStreamCommand for FromXLSX {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from xlsx").switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"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 (
|
||||
FromXLSXArgs {
|
||||
headerless: _headerless,
|
||||
noheaders: _noheaders,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
|
@ -8,7 +8,7 @@ pub struct ToCSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ToCSVArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
separator: Option<Value>,
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ impl WholeStreamCommand for ToCSV {
|
||||
Some('s'),
|
||||
)
|
||||
.switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"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 (
|
||||
ToCSVArgs {
|
||||
separator,
|
||||
headerless,
|
||||
noheaders,
|
||||
},
|
||||
input,
|
||||
) = 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)]
|
||||
|
@ -165,7 +165,7 @@ fn merge_descriptors(values: &[Value]) -> Vec<Spanned<String>> {
|
||||
}
|
||||
|
||||
pub async fn to_delimited_data(
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
sep: char,
|
||||
format_name: &'static str,
|
||||
input: InputStream,
|
||||
@ -192,7 +192,7 @@ pub async fn to_delimited_data(
|
||||
futures::stream::iter(to_process_input.into_iter().map(move |value| {
|
||||
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
||||
Ok(mut x) => {
|
||||
if headerless {
|
||||
if noheaders {
|
||||
if let Some(second_line) = x.find('\n') {
|
||||
let start = second_line + 1;
|
||||
x.replace_range(0..start, "");
|
||||
|
@ -8,7 +8,7 @@ pub struct ToTSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ToTSVArgs {
|
||||
headerless: bool,
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -19,9 +19,9 @@ impl WholeStreamCommand for ToTSV {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to tsv").switch(
|
||||
"headerless",
|
||||
"noheaders",
|
||||
"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> {
|
||||
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)]
|
||||
|
@ -64,7 +64,7 @@ fn table_to_csv_text_skipping_headers_after_conversion() {
|
||||
| str trim
|
||||
| split column "," a b c d origin
|
||||
| last 1
|
||||
| to csv --headerless
|
||||
| to csv --noheaders
|
||||
"#
|
||||
));
|
||||
|
||||
@ -198,7 +198,7 @@ fn from_csv_text_skipping_headers_to_table() {
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open los_tres_amigos.txt
|
||||
| from csv --headerless
|
||||
| from csv --noheaders
|
||||
| get Column3
|
||||
| count
|
||||
"#
|
||||
|
@ -72,7 +72,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open oc_get_svc.txt
|
||||
| from ssv --headerless -a
|
||||
| from ssv --noheaders -a
|
||||
| first
|
||||
| get Column1
|
||||
"#
|
||||
@ -82,7 +82,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open oc_get_svc.txt
|
||||
| from ssv --headerless
|
||||
| from ssv --noheaders
|
||||
| first
|
||||
| get Column1
|
||||
|
||||
|
@ -70,7 +70,7 @@ fn table_to_tsv_text_skipping_headers_after_conversion() {
|
||||
| lines
|
||||
| split column "\t" a b c d origin
|
||||
| last 1
|
||||
| to tsv --headerless
|
||||
| to tsv --noheaders
|
||||
"#
|
||||
));
|
||||
|
||||
@ -121,7 +121,7 @@ fn from_tsv_text_skipping_headers_to_table() {
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open los_tres_amigos.txt
|
||||
| from tsv --headerless
|
||||
| from tsv --noheaders
|
||||
| get Column3
|
||||
| count
|
||||
"#
|
||||
|
Loading…
Reference in New Issue
Block a user