diff --git a/crates/nu-command/src/commands/mod.rs b/crates/nu-command/src/commands/mod.rs index 94117f12a..8afaf56ce 100644 --- a/crates/nu-command/src/commands/mod.rs +++ b/crates/nu-command/src/commands/mod.rs @@ -81,8 +81,6 @@ mod tests { whole_stream_command(StrContains), whole_stream_command(StrIndexOf), whole_stream_command(StrTrim), - whole_stream_command(StrTrimLeft), - whole_stream_command(StrTrimRight), whole_stream_command(StrStartsWith), whole_stream_command(StrEndsWith), //whole_stream_command(StrCollect), diff --git a/crates/nu-command/src/commands/strings/str_/mod.rs b/crates/nu-command/src/commands/strings/str_/mod.rs index 656e8bdb3..5fcc4da11 100644 --- a/crates/nu-command/src/commands/strings/str_/mod.rs +++ b/crates/nu-command/src/commands/strings/str_/mod.rs @@ -42,6 +42,4 @@ pub use to_datetime::SubCommand as StrToDatetime; pub use to_decimal::SubCommand as StrToDecimal; pub use to_integer::SubCommand as StrToInteger; pub use trim::Trim as StrTrim; -pub use trim::TrimLeft as StrTrimLeft; -pub use trim::TrimRight as StrTrimRight; pub use upcase::SubCommand as StrUpcase; diff --git a/crates/nu-command/src/commands/strings/str_/trim/command.rs b/crates/nu-command/src/commands/strings/str_/trim/command.rs new file mode 100644 index 000000000..23d9b010b --- /dev/null +++ b/crates/nu-command/src/commands/strings/str_/trim/command.rs @@ -0,0 +1,825 @@ +use super::operate; +use crate::commands::strings::str_::trim::ClosureFlags; +use crate::prelude::*; +use nu_engine::WholeStreamCommand; +use nu_errors::ShellError; +use nu_protocol::{Signature, SyntaxShape, Value}; +pub struct SubCommand; + +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "str trim" + } + + fn signature(&self) -> Signature { + Signature::build("str trim") + .rest( + SyntaxShape::ColumnPath, + "optionally trim text by column paths", + ) + .named( + "char", + SyntaxShape::String, + "character to trim (default: whitespace)", + Some('c'), + ) + .switch( + "left", + "trims characters only from the beginning of the string (default: whitespace)", + Some('l'), + ) + .switch( + "right", + "trims characters only from the end of the string (default: whitespace)", + Some('r'), + ) + .switch( + "all", + "trims all characters from both sides of the string *and* in the middle (default: whitespace)", + Some('a'), + ) + .switch("both", "trims all characters from left and right side of the string (default: whitespace)", Some('b')) + .switch("format", "trims spaces replacing multiple characters with singles in the middle (default: whitespace)", Some('f')) + } + fn usage(&self) -> &str { + "trims text" + } + + fn run_with_actions(&self, args: CommandArgs) -> Result { + operate(args, &trim) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Trim whitespace", + example: "echo 'Nu shell ' | str trim", + result: Some(vec![Value::from("Nu shell")]), + }, + Example { + description: "Trim a specific character", + example: "echo '=== Nu shell ===' | str trim -c '=' | str trim", + result: Some(vec![Value::from("Nu shell")]), + }, + Example { + description: "Trim all characters", + example: "echo ' Nu shell ' | str trim -a", + result: Some(vec![Value::from("Nushell")]), + }, + Example { + description: "Trim whitespace from the beginning of string", + example: "echo ' Nu shell ' | str trim -l", + result: Some(vec![Value::from("Nu shell ")]), + }, + Example { + description: "Trim a specific character", + example: "echo '=== Nu shell ===' | str trim -c '='", + result: Some(vec![Value::from(" Nu shell ")]), + }, + Example { + description: "Trim whitespace from the end of string", + example: "echo ' Nu shell ' | str trim -r", + result: Some(vec![Value::from(" Nu shell")]), + }, + Example { + description: "Trim a specific character", + example: "echo '=== Nu shell ===' | str trim -r -c '='", + result: Some(vec![Value::from("=== Nu shell ")]), + }, + ] + } +} + +fn trim(s: &str, char_: Option, closure_flags: &ClosureFlags) -> String { + let ClosureFlags { + left_trim, + right_trim, + all_flag, + both_flag, + format_flag, + } = closure_flags; + let delimiter = char_.unwrap_or(' '); + let mut buf = vec![]; + let mut is_delim = false; + let left_remove = left_trim | all_flag | both_flag | format_flag; + let right_remove = right_trim | all_flag | both_flag | format_flag; + let middle_remove = all_flag | format_flag; + let middle_add = !all_flag && (*format_flag); // cases like -a -f + for c in s.chars() { + match c { + x if x == delimiter && buf.is_empty() && left_remove => continue, + x if x == delimiter => { + is_delim = true; + if !middle_remove { + buf.push(delimiter); + } + } + _ => { + if is_delim && middle_add { + buf.push(delimiter); + is_delim = false; + } + buf.push(c); + } + } + } + if right_remove { + while buf.last() == Some(&delimiter) { + buf.pop(); + } + } + buf.into_iter().collect() +} + +#[cfg(test)] +mod tests { + use super::ShellError; + use super::SubCommand; + use crate::commands::strings::str_::trim::command::trim; + use crate::commands::strings::str_::trim::{action, ActionMode, ClosureFlags}; + use nu_protocol::row; + use nu_source::Tag; + use nu_test_support::value::{int, string, table}; + + #[test] + fn examples_work_as_expected() -> Result<(), ShellError> { + use crate::examples::test as test_examples; + test_examples(SubCommand {}) + } + + #[test] + fn trims() { + let word = string("andres "); + let expected = string("andres"); + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_global() { + let word = string(" global "); + let expected = string("global"); + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_ignores_numbers() { + let number = int(2020); + let expected = int(2020); + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &number, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_row() { + let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; + let expected = row!["a".to_string() => string("c"), " b ".to_string() => string("d")]; + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_table() { + let row = table(&[string(" a "), int(65), string(" d")]); + let expected = table(&[string("a"), int(65), string("d")]); + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_custom_character_both_ends() { + let word = string("!#andres#!"); + let expected = string("#andres#"); + let closure_flags = ClosureFlags { + both_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some('!'), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + #[test] + fn trims_all_white_space() { + let word = string(" Value1 a lot of spaces "); + let expected = string("Value1alotofspaces"); + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some(' '), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trims_row_all_white_space() { + let row = row!["a".to_string() => string(" nu shell "), " b ".to_string() => string(" b c d e ")]; + let expected = + row!["a".to_string() => string("nushell"), " b ".to_string() => string("bcde")]; + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trims_table_all_white_space() { + let row = table(&[string(" nu shell "), int(65), string(" d")]); + let expected = table(&[string("nushell"), int(65), string("d")]); + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_all_custom_character() { + let word = string(".Value1.a.lot..of...dots."); + let expected = string("Value1alotofdots"); + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some('.'), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trims_row_all_custom_character() { + let row = row!["a".to_string() => string("!!!!nu!!shell!!!"), " b ".to_string() => string("!!b!c!!d!e!!")]; + let expected = + row!["a".to_string() => string("nushell"), " b ".to_string() => string("bcde")]; + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + Some('!'), + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trims_table_all_custom_character() { + let row = table(&[string("##nu####shell##"), int(65), string("#d")]); + let expected = table(&[string("nushell"), int(65), string("d")]); + let closure_flags = ClosureFlags { + all_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + Some('#'), + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + #[test] + fn trims_whitespace_from_left() { + let word = string(" andres "); + let expected = string("andres "); + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_left_ignores_numbers() { + let number = int(2020); + let expected = int(2020); + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &number, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_left_global() { + let word = string(" global "); + let expected = string("global "); + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_left_row() { + let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; + let expected = row!["a".to_string() => string("c "), " b ".to_string() => string("d ")]; + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_left_table() { + let row = table(&[string(" a "), int(65), string(" d")]); + let expected = table(&[string("a "), int(65), string("d")]); + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_custom_chars_from_left() { + let word = string("!!! andres !!!"); + let expected = string(" andres !!!"); + let closure_flags = ClosureFlags { + left_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some('!'), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + #[test] + fn trims_whitespace_from_right() { + let word = string(" andres "); + let expected = string(" andres"); + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_right_global() { + let word = string(" global "); + let expected = string(" global"); + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_right_ignores_numbers() { + let number = int(2020); + let expected = int(2020); + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &number, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_right_row() { + let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; + let expected = row!["a".to_string() => string(" c"), " b ".to_string() => string(" d")]; + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_right_table() { + let row = table(&[string(" a "), int(65), string(" d")]); + let expected = table(&[string(" a"), int(65), string(" d")]); + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_custom_chars_from_right() { + let word = string("#@! andres !@#"); + let expected = string("#@! andres !@"); + let closure_flags = ClosureFlags { + right_trim: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some('#'), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_whitespace_format_flag() { + let word = string(" nushell is great "); + let expected = string("nushell is great"); + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some(' '), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_format_flag_global() { + let word = string("global "); + let expected = string("global"); + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some(' '), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + #[test] + fn global_trim_format_flag_ignores_numbers() { + let number = int(2020); + let expected = int(2020); + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &number, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_format_flag_row() { + let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" b c d e ")]; + let expected = row!["a".to_string() => string("c"), " b ".to_string() => string("b c d e")]; + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn global_trim_format_flag_table() { + let row = table(&[ + string(" a b c d "), + int(65), + string(" b c d e f"), + ]); + let expected = table(&[string("a b c d"), int(65), string("b c d e f")]); + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &row, + Tag::unknown(), + None, + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_custom_chars_format_flag() { + let word = string(".Value1.a..lot...of....dots."); + let expected = string("Value1.a.lot.of.dots"); + let closure_flags = ClosureFlags { + format_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some('.'), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_all_format_flag_whitespace() { + let word = string(" nushell is great "); + let expected = string("nushellisgreat"); + let closure_flags = ClosureFlags { + format_flag: true, + all_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some(' '), + &closure_flags, + &trim, + ActionMode::Local, + ) + .unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn trims_all_format_flag_global() { + let word = string(" nushell is great "); + let expected = string("nushellisgreat"); + let closure_flags = ClosureFlags { + format_flag: true, + all_flag: true, + ..Default::default() + }; + + let actual = action( + &word, + Tag::unknown(), + Some(' '), + &closure_flags, + &trim, + ActionMode::Global, + ) + .unwrap(); + assert_eq!(actual, expected); + } +} diff --git a/crates/nu-command/src/commands/strings/str_/trim/mod.rs b/crates/nu-command/src/commands/strings/str_/trim/mod.rs index af1dd5735..115b3572d 100644 --- a/crates/nu-command/src/commands/strings/str_/trim/mod.rs +++ b/crates/nu-command/src/commands/strings/str_/trim/mod.rs @@ -1,7 +1,4 @@ -mod trim_both_ends; -mod trim_left; -mod trim_right; - +mod command; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::ShellTypeName; @@ -10,29 +7,45 @@ use nu_source::{Tag, Tagged}; use nu_value_ext::ValueExt; use std::iter::FromIterator; -pub use trim_both_ends::SubCommand as Trim; -pub use trim_left::SubCommand as TrimLeft; -pub use trim_right::SubCommand as TrimRight; +pub use command::SubCommand as Trim; struct Arguments { character: Option>, column_paths: Vec, } +#[derive(Default, Debug, Copy, Clone)] +pub struct ClosureFlags { + all_flag: bool, + left_trim: bool, + right_trim: bool, + format_flag: bool, + both_flag: bool, +} + pub fn operate(args: CommandArgs, trim_operation: &'static F) -> Result where - F: Fn(&str, Option) -> String + Send + Sync + 'static, + F: Fn(&str, Option, &ClosureFlags) -> String + Send + Sync + 'static, { - let (options, input) = ( + let (options, closure_flags, input) = ( Arc::new(Arguments { character: args.get_flag("char")?, column_paths: args.rest(0)?, }), + ClosureFlags { + all_flag: args.has_flag("all"), + left_trim: args.has_flag("left"), + right_trim: args.has_flag("right"), + format_flag: args.has_flag("format"), + both_flag: args.has_flag("both") + || (!args.has_flag("all") + && !args.has_flag("left") + && !args.has_flag("right") + && !args.has_flag("format")), // this is the case if no flags are provided + }, args.input, ); - let to_trim = options.character.as_ref().map(|tagged| tagged.item); - Ok(input .map(move |v| { if options.column_paths.is_empty() { @@ -40,6 +53,7 @@ where &v, v.tag(), to_trim, + &closure_flags, &trim_operation, ActionMode::Global, )?) @@ -50,7 +64,14 @@ where ret = ret.swap_data_by_column_path( path, Box::new(move |old| { - action(old, old.tag(), to_trim, &trim_operation, ActionMode::Local) + action( + old, + old.tag(), + to_trim, + &closure_flags, + &trim_operation, + ActionMode::Local, + ) }), )?; } @@ -71,16 +92,17 @@ pub fn action( input: &Value, tag: impl Into, char_: Option, + closure_flags: &ClosureFlags, trim_operation: &F, mode: ActionMode, ) -> Result where - F: Fn(&str, Option) -> String + Send + Sync + 'static, + F: Fn(&str, Option, &ClosureFlags) -> String + Send + Sync + 'static, { let tag = tag.into(); match &input.value { UntaggedValue::Primitive(Primitive::String(s)) => { - Ok(UntaggedValue::string(trim_operation(s, char_)).into_value(tag)) + Ok(UntaggedValue::string(trim_operation(s, char_, closure_flags)).into_value(tag)) } other => match mode { ActionMode::Global => match other { @@ -91,7 +113,7 @@ where .map(|(k, v)| -> Result<_, ShellError> { Ok(( k.clone(), - action(v, tag.clone(), char_, trim_operation, mode)?, + action(v, tag.clone(), char_, closure_flags, trim_operation, mode)?, )) }) .collect(); @@ -102,7 +124,7 @@ where let values: Result, ShellError> = values .iter() .map(|v| -> Result<_, ShellError> { - action(v, tag.clone(), char_, trim_operation, mode) + action(v, tag.clone(), char_, closure_flags, trim_operation, mode) }) .collect(); Ok(UntaggedValue::Table(values?).into_value(tag)) diff --git a/crates/nu-command/src/commands/strings/str_/trim/trim_both_ends.rs b/crates/nu-command/src/commands/strings/str_/trim/trim_both_ends.rs deleted file mode 100644 index a0a56a1e2..000000000 --- a/crates/nu-command/src/commands/strings/str_/trim/trim_both_ends.rs +++ /dev/null @@ -1,127 +0,0 @@ -use super::operate; -use crate::prelude::*; -use nu_engine::WholeStreamCommand; -use nu_errors::ShellError; -use nu_protocol::{Signature, SyntaxShape, Value}; - -pub struct SubCommand; - -impl WholeStreamCommand for SubCommand { - fn name(&self) -> &str { - "str trim" - } - - fn signature(&self) -> Signature { - Signature::build("str trim") - .rest( - SyntaxShape::ColumnPath, - "optionally trim text by column paths", - ) - .named( - "char", - SyntaxShape::String, - "character to trim (default: whitespace)", - Some('c'), - ) - } - - fn usage(&self) -> &str { - "trims text" - } - - fn run_with_actions(&self, args: CommandArgs) -> Result { - operate(args, &trim) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Trim whitespace", - example: "echo 'Nu shell ' | str trim", - result: Some(vec![Value::from("Nu shell")]), - }, - Example { - description: "Trim a specific character", - example: "echo '=== Nu shell ===' | str trim -c '=' | str trim", - result: Some(vec![Value::from("Nu shell")]), - }, - ] - } -} -fn trim(s: &str, char_: Option) -> String { - match char_ { - None => String::from(s.trim()), - Some(ch) => String::from(s.trim_matches(ch)), - } -} - -#[cfg(test)] -mod tests { - use super::ShellError; - use super::{trim, SubCommand}; - use crate::commands::strings::str_::trim::{action, ActionMode}; - use nu_protocol::row; - use nu_source::Tag; - use nu_test_support::value::{int, string, table}; - - #[test] - fn examples_work_as_expected() -> Result<(), ShellError> { - use crate::examples::test as test_examples; - - test_examples(SubCommand {}) - } - - #[test] - fn trims() { - let word = string("andres "); - let expected = string("andres"); - - let actual = action(&word, Tag::unknown(), None, &trim, ActionMode::Local).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_global() { - let word = string(" global "); - let expected = string("global"); - - let actual = action(&word, Tag::unknown(), None, &trim, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_ignores_numbers() { - let number = int(2020); - let expected = int(2020); - - let actual = action(&number, Tag::unknown(), None, &trim, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_row() { - let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; - let expected = row!["a".to_string() => string("c"), " b ".to_string() => string("d")]; - - let actual = action(&row, Tag::unknown(), None, &trim, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_table() { - let row = table(&[string(" a "), int(65), string(" d")]); - let expected = table(&[string("a"), int(65), string("d")]); - - let actual = action(&row, Tag::unknown(), None, &trim, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_custom_character_both_ends() { - let word = string("!#andres#!"); - let expected = string("#andres#"); - - let actual = action(&word, Tag::unknown(), Some('!'), &trim, ActionMode::Local).unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/crates/nu-command/src/commands/strings/str_/trim/trim_left.rs b/crates/nu-command/src/commands/strings/str_/trim/trim_left.rs deleted file mode 100644 index 8c1ffb3da..000000000 --- a/crates/nu-command/src/commands/strings/str_/trim/trim_left.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::operate; -use crate::prelude::*; -use nu_engine::WholeStreamCommand; -use nu_errors::ShellError; -use nu_protocol::{Signature, SyntaxShape, Value}; - -pub struct SubCommand; - -impl WholeStreamCommand for SubCommand { - fn name(&self) -> &str { - "str ltrim" - } - - fn signature(&self) -> Signature { - Signature::build("str ltrim") - .rest( - SyntaxShape::ColumnPath, - "optionally trim text starting from the beginning by column paths", - ) - .named( - "char", - SyntaxShape::String, - "character to trim (default: whitespace)", - Some('c'), - ) - } - - fn usage(&self) -> &str { - "trims whitespace or character from the beginning of text" - } - - fn run_with_actions(&self, args: CommandArgs) -> Result { - operate(args, &trim_left) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Trim whitespace from the beginning of string", - example: "echo ' Nu shell ' | str ltrim", - result: Some(vec![Value::from("Nu shell ")]), - }, - Example { - description: "Trim a specific character", - example: "echo '=== Nu shell ===' | str ltrim -c '='", - result: Some(vec![Value::from(" Nu shell ===")]), - }, - ] - } -} - -fn trim_left(s: &str, char_: Option) -> String { - match char_ { - None => String::from(s.trim_start()), - Some(ch) => String::from(s.trim_start_matches(ch)), - } -} - -#[cfg(test)] -mod tests { - use super::ShellError; - use super::{trim_left, SubCommand}; - use crate::commands::strings::str_::trim::{action, ActionMode}; - use nu_protocol::row; - use nu_source::Tag; - use nu_test_support::value::{int, string, table}; - - #[test] - fn examples_work_as_expected() -> Result<(), ShellError> { - use crate::examples::test as test_examples; - - test_examples(SubCommand {}) - } - - #[test] - fn trims_whitespace_from_left() { - let word = string(" andres "); - let expected = string("andres "); - - let actual = action(&word, Tag::unknown(), None, &trim_left, ActionMode::Local).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_left_global() { - let word = string(" global "); - let expected = string("global "); - - let actual = action(&word, Tag::unknown(), None, &trim_left, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_left_ignores_numbers() { - let number = int(2020); - let expected = int(2020); - - let actual = action( - &number, - Tag::unknown(), - None, - &trim_left, - ActionMode::Global, - ) - .unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_left_row() { - let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; - let expected = row!["a".to_string() => string("c "), " b ".to_string() => string("d ")]; - - let actual = action(&row, Tag::unknown(), None, &trim_left, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_left_table() { - let row = table(&[string(" a "), int(65), string(" d")]); - let expected = table(&[string("a "), int(65), string("d")]); - - let actual = action(&row, Tag::unknown(), None, &trim_left, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_custom_chars_from_left() { - let word = string("!!! andres !!!"); - let expected = string(" andres !!!"); - - let actual = action( - &word, - Tag::unknown(), - Some('!'), - &trim_left, - ActionMode::Local, - ) - .unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/crates/nu-command/src/commands/strings/str_/trim/trim_right.rs b/crates/nu-command/src/commands/strings/str_/trim/trim_right.rs deleted file mode 100644 index 165805ec4..000000000 --- a/crates/nu-command/src/commands/strings/str_/trim/trim_right.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::operate; -use crate::prelude::*; -use nu_engine::WholeStreamCommand; -use nu_errors::ShellError; -use nu_protocol::{Signature, SyntaxShape, Value}; - -pub struct SubCommand; - -impl WholeStreamCommand for SubCommand { - fn name(&self) -> &str { - "str rtrim" - } - - fn signature(&self) -> Signature { - Signature::build("str rtrim") - .rest( - SyntaxShape::ColumnPath, - "optionally trim text starting from the end by column paths", - ) - .named( - "char", - SyntaxShape::String, - "character to trim (default: whitespace)", - Some('c'), - ) - } - - fn usage(&self) -> &str { - "trims whitespace or character from the end of text" - } - - fn run_with_actions(&self, args: CommandArgs) -> Result { - operate(args, &trim_right) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Trim whitespace from the end of string", - example: "echo ' Nu shell ' | str rtrim", - result: Some(vec![Value::from(" Nu shell")]), - }, - Example { - description: "Trim a specific character", - example: "echo '=== Nu shell ===' | str rtrim -c '='", - result: Some(vec![Value::from("=== Nu shell ")]), - }, - ] - } -} - -fn trim_right(s: &str, char_: Option) -> String { - match char_ { - None => String::from(s.trim_end()), - Some(ch) => String::from(s.trim_end_matches(ch)), - } -} - -#[cfg(test)] -mod tests { - use super::ShellError; - use super::{trim_right, SubCommand}; - use crate::commands::strings::str_::trim::{action, ActionMode}; - use nu_protocol::row; - use nu_source::Tag; - use nu_test_support::value::{int, string, table}; - - #[test] - fn examples_work_as_expected() -> Result<(), ShellError> { - use crate::examples::test as test_examples; - - test_examples(SubCommand {}) - } - - #[test] - fn trims_whitespace_from_right() { - let word = string(" andres "); - let expected = string(" andres"); - - let actual = action(&word, Tag::unknown(), None, &trim_right, ActionMode::Local).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_right_global() { - let word = string(" global "); - let expected = string(" global"); - - let actual = action(&word, Tag::unknown(), None, &trim_right, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_right_ignores_numbers() { - let number = int(2020); - let expected = int(2020); - - let actual = action( - &number, - Tag::unknown(), - None, - &trim_right, - ActionMode::Global, - ) - .unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_row() { - let row = row!["a".to_string() => string(" c "), " b ".to_string() => string(" d ")]; - let expected = row!["a".to_string() => string(" c"), " b ".to_string() => string(" d")]; - - let actual = action(&row, Tag::unknown(), None, &trim_right, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn global_trim_table() { - let row = table(&[string(" a "), int(65), string(" d")]); - let expected = table(&[string(" a"), int(65), string(" d")]); - - let actual = action(&row, Tag::unknown(), None, &trim_right, ActionMode::Global).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn trims_custom_chars_from_right() { - let word = string("#@! andres !@#"); - let expected = string("#@! andres !@"); - - let actual = action( - &word, - Tag::unknown(), - Some('#'), - &trim_right, - ActionMode::Local, - ) - .unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 06142a6c1..c17e1bd43 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -103,8 +103,6 @@ pub fn create_default_context(interactive: bool) -> Result