From a62745eefb889688f1447e0d9637049987ff0f6f Mon Sep 17 00:00:00 2001 From: rimathia Date: Sun, 26 Apr 2020 19:26:02 +0200 Subject: [PATCH] make trim apply to strings contained in tables (also at deeper nesting (#1664) * make trim apply to strings contained in tables (also at deeper nesting levels), not just top-level strings * remove unnecessary clone (thanks clippy) --- crates/nu-cli/src/commands/trim.rs | 43 ++++++++++++-- crates/nu-cli/src/prelude.rs | 1 - crates/nu-cli/tests/commands/mod.rs | 1 + crates/nu-cli/tests/commands/trim.rs | 85 ++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 crates/nu-cli/tests/commands/trim.rs diff --git a/crates/nu-cli/src/commands/trim.rs b/crates/nu-cli/src/commands/trim.rs index eb72dbac49..cbc3f6712a 100644 --- a/crates/nu-cli/src/commands/trim.rs +++ b/crates/nu-cli/src/commands/trim.rs @@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; +use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; pub struct Trim; @@ -28,12 +28,47 @@ impl WholeStreamCommand for Trim { } } +fn trim_primitive(p: &mut Primitive) { + match p { + Primitive::String(s) | Primitive::Line(s) => *p = Primitive::String(s.trim().to_string()), + Primitive::Nothing + | Primitive::Int(_) + | Primitive::Decimal(_) + | Primitive::Bytes(_) + | Primitive::ColumnPath(_) + | Primitive::Pattern(_) + | Primitive::Boolean(_) + | Primitive::Date(_) + | Primitive::Duration(_) + | Primitive::Range(_) + | Primitive::Path(_) + | Primitive::Binary(_) + | Primitive::BeginningOfStream + | Primitive::EndOfStream => (), + } +} + +fn trim_row(d: &mut Dictionary) { + for (_, mut value) in d.entries.iter_mut() { + trim_value(&mut value); + } +} + +fn trim_value(v: &mut Value) { + match &mut v.value { + UntaggedValue::Primitive(p) => trim_primitive(p), + UntaggedValue::Row(row) => trim_row(row), + _ => (), + }; +} + fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result { Ok(args .input - .map(move |v| { - let string = String::extract(&v)?; - ReturnSuccess::value(UntaggedValue::string(string.trim()).into_value(v.tag())) + .map(|v| { + let mut trimmed = v; + trim_value(&mut trimmed); + ReturnSuccess::value(trimmed) }) .to_output_stream()) } diff --git a/crates/nu-cli/src/prelude.rs b/crates/nu-cli/src/prelude.rs index ed9c391c09..4ce9d287e2 100644 --- a/crates/nu-cli/src/prelude.rs +++ b/crates/nu-cli/src/prelude.rs @@ -78,7 +78,6 @@ pub(crate) use crate::commands::command::{ pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::Context; pub(crate) use crate::data::config; -pub(crate) use crate::data::types::ExtractType; pub(crate) use crate::data::value; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; diff --git a/crates/nu-cli/tests/commands/mod.rs b/crates/nu-cli/tests/commands/mod.rs index 596e5c02e9..aa1ac93ad9 100644 --- a/crates/nu-cli/tests/commands/mod.rs +++ b/crates/nu-cli/tests/commands/mod.rs @@ -37,6 +37,7 @@ mod split_by; mod split_column; mod sum; mod touch; +mod trim; mod uniq; mod where_; mod wrap; diff --git a/crates/nu-cli/tests/commands/trim.rs b/crates/nu-cli/tests/commands/trim.rs new file mode 100644 index 0000000000..1c5bf0ae5a --- /dev/null +++ b/crates/nu-cli/tests/commands/trim.rs @@ -0,0 +1,85 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn string() { + Playground::setup("trim_test_1", |dirs, _sandbox| { + let test_strings = ["\n", " \n ", "\thi\n\n", "\u{2003}a"]; + assert!(test_strings[3].chars().count() == 2); + + for test_string in &test_strings { + let commandline = format!( + r#" + echo {} + | trim + "#, + test_string + ); + let actual = nu!( + cwd: dirs.test(), pipeline(&commandline + )); + assert_eq!(actual, test_string.trim()) + } + }) +} + +#[test] +fn row() { + Playground::setup("trim_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + FileWithContent("lines.csv", "lines\n l0\n\tl1\n l2\t \n\n"), + FileWithContent("lines_trimmed.csv", "lines\nl0\nl1\nl2\n"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open lines.csv + | trim + "# + )); + + let expected = nu!( + cwd: dirs.test(), pipeline( + r#" + open lines_trimmed.csv + "# + )); + + assert_eq!(actual, expected) + }) +} + +#[test] +fn nested() { + Playground::setup("trim_test_3", |dirs, sandbox| { + sandbox.with_files(vec![ + FileWithContent( + "nested.json", + r#"{ "l0" : {"l1": {"l2" : {"a" : " s0", "b" : "\t\ts1\n"} } } }"#, + ), + FileWithContent( + "nested_trimmed.json", + r#"{ "l0" : {"l1": {"l2" : {"a" : "s0", "b" : "s1"} } } }"#, + ), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nested.json + | trim + "# + )); + + let expected = nu!( + cwd: dirs.test(), pipeline( + r#" + open nested_trimmed.json + "# + )); + + assert_eq!(actual, expected) + }) +}