From 81affaa584079c8e981accee9ae390c1e0e32c23 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 19:10:38 +0200 Subject: [PATCH 1/9] Adds tests for allowed-spaces option. --- src/commands/from_ssv.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 1be9b4567..3c61e211f 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -184,4 +184,43 @@ mod tests { let result = string_to_table(input, true); assert_eq!(result, None); } + + #[test] + fn it_allows_a_predefined_number_of_spaces() { + let input = r#" + column a column b + entry 1 entry number 2 + 3 four + "#; + + let result = string_to_table(input, false); + assert_eq!( + result, + Some(vec![ + vec![ + owned("column a", "entry 1"), + owned("column b", "entry number 2") + ], + vec![owned("column a", "3"), owned("column b", "four")] + ]) + ); + } + + #[test] + fn it_trims_remaining_separator_space() { + let input = r#" + colA colB colC + val1 val2 val3 + "#; + + let trimmed = |s: &str| s.trim() == s; + + let result = string_to_table(input, false).unwrap(); + assert_eq!( + true, + result + .iter() + .all(|row| row.iter().all(|(a, b)| trimmed(a) && trimmed(b))) + ) + } } From d32e97b81288297d02e1f044fc1216765ee78bf5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 19:52:12 +0200 Subject: [PATCH 2/9] Implements variable space separator length, version 1. --- src/commands/from_ssv.rs | 44 +++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 3c61e211f..6d2dcfda5 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -7,9 +7,11 @@ pub struct FromSSV; #[derive(Deserialize)] pub struct FromSSVArgs { headerless: bool, + n: Option>, } const STRING_REPRESENTATION: &str = "from-ssv"; +const DEFAULT_ALLOWED_SPACES: usize = 0; impl WholeStreamCommand for FromSSV { fn name(&self) -> &str { @@ -17,7 +19,9 @@ impl WholeStreamCommand for FromSSV { } fn signature(&self) -> Signature { - Signature::build(STRING_REPRESENTATION).switch("headerless") + Signature::build(STRING_REPRESENTATION) + .switch("headerless") + .named("n", SyntaxShape::Int) } fn usage(&self) -> &str { @@ -33,12 +37,19 @@ impl WholeStreamCommand for FromSSV { } } -fn string_to_table(s: &str, headerless: bool) -> Option>> { +fn string_to_table( + s: &str, + headerless: bool, + split_at: usize, +) -> Option>> { let mut lines = s.lines().filter(|l| !l.trim().is_empty()); + let separator = " ".repeat(std::cmp::max(split_at, 1)); let headers = lines .next()? - .split_whitespace() + .split(&separator) + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) .map(|s| s.to_owned()) .collect::>(); @@ -55,7 +66,11 @@ fn string_to_table(s: &str, headerless: bool) -> Option Option, ) -> Option> { let tag = tag.into(); - let rows = string_to_table(s, headerless)? + let rows = string_to_table(s, headerless, split_at)? .iter() .map(|row| { let mut tagged_dict = TaggedDictBuilder::new(&tag); @@ -87,13 +103,17 @@ fn from_ssv_string_to_value( } fn from_ssv( - FromSSVArgs { headerless }: FromSSVArgs, + FromSSVArgs { headerless, n }: FromSSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); let mut latest_tag: Option = None; + let split_at = match n { + Some(number) => number.item, + None => DEFAULT_ALLOWED_SPACES + }; for value in values { let value_tag = value.tag(); @@ -112,7 +132,7 @@ fn from_ssv( } } - match from_ssv_string_to_value(&concat_string, headerless, name.clone()) { + match from_ssv_string_to_value(&concat_string, headerless, split_at, name.clone()) { Some(x) => match x { Tagged { item: Value::Table(list), ..} => { for l in list { yield ReturnSuccess::value(l) } @@ -151,7 +171,7 @@ mod tests { 3 4 "#; - let result = string_to_table(input, false); + let result = string_to_table(input, false, 1); assert_eq!( result, Some(vec![ @@ -168,7 +188,7 @@ mod tests { 1 2 3 4 "#; - let result = string_to_table(input, true); + let result = string_to_table(input, true, 1); assert_eq!( result, Some(vec![ @@ -181,7 +201,7 @@ mod tests { #[test] fn it_returns_none_given_an_empty_string() { let input = ""; - let result = string_to_table(input, true); + let result = string_to_table(input, true, 1); assert_eq!(result, None); } @@ -193,7 +213,7 @@ mod tests { 3 four "#; - let result = string_to_table(input, false); + let result = string_to_table(input, false, 3); assert_eq!( result, Some(vec![ @@ -215,7 +235,7 @@ mod tests { let trimmed = |s: &str| s.trim() == s; - let result = string_to_table(input, false).unwrap(); + let result = string_to_table(input, false, 2).unwrap(); assert_eq!( true, result From e7b37bee08d8d15af22a9116240d10f617f0be8f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 20:58:46 +0200 Subject: [PATCH 3/9] Adds filter test for named param. --- tests/filters_test.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index ed841af4c..a84622c37 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -383,6 +383,34 @@ fn converts_from_ssv_text_to_structured_table() { }) } +#[test] +fn converts_from_ssv_text_to_structured_table_with_separator_specified() { + Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.txt", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open oc_get_svc.txt + | from-ssv -n 3 + | nth 0 + | get IP + | echo $it + "# + )); + + assert_eq!(actual, "172.30.78.158"); + }) +} + #[test] fn converts_from_ssv_text_skipping_headers_to_structured_table() { Playground::setup("filter_from_ssv_test_2", |dirs, sandbox| { From b4c639a5d9b286a74e822a800f5fc27e48140ee6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 21:01:14 +0200 Subject: [PATCH 4/9] Updates description of command in readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cc54584f..33d381d71 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-ini | Parse text as .ini and create table | | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | -| from-ssv | Parse text as whitespace-separated values and create table| +| from-ssv -n | Parse text as space-separated values and create table | | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | | from-url | Parse urlencoded string and create a table | From 294c2c600ddf098b2106a0634922b559338ee463 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 21:10:15 +0200 Subject: [PATCH 5/9] Update the usage string to match the readme. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 6d2dcfda5..0d56c20e0 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -25,7 +25,7 @@ impl WholeStreamCommand for FromSSV { } fn usage(&self) -> &str { - "Parse text as whitespace-separated values and create a table." + "Parse text as space-separated values and create a table." } fn run( From 0d2044e72e21d3a44a034b9eeb64de783cdd323c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 22:05:32 +0200 Subject: [PATCH 6/9] Changes flag to `minimum-spaces`. --- src/commands/from_ssv.rs | 12 ++++++++---- tests/filters_test.rs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 0d56c20e0..4cbb3c78f 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -7,7 +7,8 @@ pub struct FromSSV; #[derive(Deserialize)] pub struct FromSSVArgs { headerless: bool, - n: Option>, + #[serde(rename(deserialize = "minimum-spaces"))] + minimum_spaces: Option>, } const STRING_REPRESENTATION: &str = "from-ssv"; @@ -21,7 +22,7 @@ impl WholeStreamCommand for FromSSV { fn signature(&self) -> Signature { Signature::build(STRING_REPRESENTATION) .switch("headerless") - .named("n", SyntaxShape::Int) + .named("minimum-spaces", SyntaxShape::Int) } fn usage(&self) -> &str { @@ -103,14 +104,17 @@ fn from_ssv_string_to_value( } fn from_ssv( - FromSSVArgs { headerless, n }: FromSSVArgs, + FromSSVArgs { + headerless, + minimum_spaces, + }: FromSSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); let mut latest_tag: Option = None; - let split_at = match n { + let split_at = match minimum_spaces { Some(number) => number.item, None => DEFAULT_ALLOWED_SPACES }; diff --git a/tests/filters_test.rs b/tests/filters_test.rs index a84622c37..f0d5dead6 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -400,7 +400,7 @@ fn converts_from_ssv_text_to_structured_table_with_separator_specified() { cwd: dirs.test(), h::pipeline( r#" open oc_get_svc.txt - | from-ssv -n 3 + | from-ssv --minimum-spaces 3 | nth 0 | get IP | echo $it From f8d44e732bc103caf4c963f1c0ef34511704a206 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 22:05:47 +0200 Subject: [PATCH 7/9] Updates default minimum spaces to allow single spaces by default. --- src/commands/from_ssv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 4cbb3c78f..001ea8d0c 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -12,7 +12,7 @@ pub struct FromSSVArgs { } const STRING_REPRESENTATION: &str = "from-ssv"; -const DEFAULT_ALLOWED_SPACES: usize = 0; +const DEFAULT_MINIMUM_SPACES: usize = 2; impl WholeStreamCommand for FromSSV { fn name(&self) -> &str { @@ -116,7 +116,7 @@ fn from_ssv( let mut latest_tag: Option = None; let split_at = match minimum_spaces { Some(number) => number.item, - None => DEFAULT_ALLOWED_SPACES + None => DEFAULT_MINIMUM_SPACES }; for value in values { From 587bb13be55f030d83459d78c88841886453ffbc Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 23:19:16 +0200 Subject: [PATCH 8/9] Updates readme with new name of flag. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33d381d71..cf36fd2cb 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-ini | Parse text as .ini and create table | | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | -| from-ssv -n | Parse text as space-separated values and create table | +| from-ssv --minimum-spaces | Parse text as space-separated values and create table | | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | | from-url | Parse urlencoded string and create a table | From 74b0e4e5413fdb75b037630f52277540fcfa8270 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 23:20:06 +0200 Subject: [PATCH 9/9] Adds more info to the usage string. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 001ea8d0c..7aca35096 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -26,7 +26,7 @@ impl WholeStreamCommand for FromSSV { } fn usage(&self) -> &str { - "Parse text as space-separated values and create a table." + "Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2." } fn run(