From 10768b6ecfa25a1f49a1123d18dce219e892294c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Fri, 24 Apr 2020 16:37:58 -0500 Subject: [PATCH] str plugin can capitalize and trim strings. (#1652) * Str plugin can capitalize. * Str plugin can trim. --- crates/nu_plugin_str/src/nu/mod.rs | 8 +++ crates/nu_plugin_str/src/nu/tests.rs | 76 ++++++++++++++++++++++++++++ crates/nu_plugin_str/src/strutils.rs | 44 +++++++++++++++- docs/commands/str.md | 16 +++++- tests/plugins/core_str.rs | 52 ++++++++++++++++--- 5 files changed, 188 insertions(+), 8 deletions(-) diff --git a/crates/nu_plugin_str/src/nu/mod.rs b/crates/nu_plugin_str/src/nu/mod.rs index b8d12f4c2f..22ee89884b 100644 --- a/crates/nu_plugin_str/src/nu/mod.rs +++ b/crates/nu_plugin_str/src/nu/mod.rs @@ -15,9 +15,11 @@ impl Plugin for Str { fn config(&mut self) -> Result { Ok(Signature::build("str") .desc("Apply string function. Optional use the column of a table") + .switch("capitalize", "capitalizes the string", Some('c')) .switch("downcase", "convert string to lowercase", Some('d')) .switch("upcase", "convert string to uppercase", Some('U')) .switch("to-int", "convert string to integer", Some('i')) + .switch("trim", "trims the string", Some('t')) .named( "replace", SyntaxShape::String, @@ -49,6 +51,12 @@ impl Plugin for Str { fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { let args = call_info.args; + if args.has("trim") { + self.for_trim(); + } + if args.has("capitalize") { + self.for_capitalize(); + } if args.has("downcase") { self.for_downcase(); } diff --git a/crates/nu_plugin_str/src/nu/tests.rs b/crates/nu_plugin_str/src/nu/tests.rs index e69c7990ee..743f943611 100644 --- a/crates/nu_plugin_str/src/nu/tests.rs +++ b/crates/nu_plugin_str/src/nu/tests.rs @@ -56,6 +56,20 @@ mod integration { }); } + #[test] + fn picks_up_trim_flag() { + plugin(&mut Str::new()) + .args(CallStub::new().with_long_flag("trim").create()) + .setup(|plugin, _| plugin.expect_action(Action::Trim)); + } + + #[test] + fn picks_up_capitalize_flag() { + plugin(&mut Str::new()) + .args(CallStub::new().with_long_flag("capitalize").create()) + .setup(|plugin, _| plugin.expect_action(Action::Capitalize)); + } + #[test] fn picks_up_downcase_flag() { plugin(&mut Str::new()) @@ -169,6 +183,44 @@ mod integration { Ok(()) } + #[test] + fn trims_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> { + let run = plugin(&mut Str::new()) + .args( + CallStub::new() + .with_long_flag("trim") + .with_parameter("name")? + .create(), + ) + .input(structured_sample_record("name", "andres ")) + .setup(|_, _| {}) + .test(); + + let actual = expect_return_value_at(run, 0); + + assert_eq!(get_data(actual, "name"), string("andres")); + Ok(()) + } + + #[test] + fn capitalizes_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> { + let run = plugin(&mut Str::new()) + .args( + CallStub::new() + .with_long_flag("capitalize") + .with_parameter("name")? + .create(), + ) + .input(structured_sample_record("name", "andres")) + .setup(|_, _| {}) + .test(); + + let actual = expect_return_value_at(run, 0); + + assert_eq!(get_data(actual, "name"), string("Andres")); + Ok(()) + } + #[test] fn downcases_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> { let run = plugin(&mut Str::new()) @@ -261,6 +313,30 @@ mod integration { assert_eq!(actual, string("JOANDREHUDA")); } + #[test] + fn trims_the_input() { + let run = plugin(&mut Str::new()) + .args(CallStub::new().with_long_flag("trim").create()) + .input(unstructured_sample_record("andres ")) + .setup(|_, _| {}) + .test(); + + let actual = expect_return_value_at(run, 0); + assert_eq!(actual, string("andres")); + } + + #[test] + fn capitalizes_the_input() { + let run = plugin(&mut Str::new()) + .args(CallStub::new().with_long_flag("capitalize").create()) + .input(unstructured_sample_record("andres")) + .setup(|_, _| {}) + .test(); + + let actual = expect_return_value_at(run, 0); + assert_eq!(actual, string("Andres")); + } + #[test] fn downcases_the_input() { let run = plugin(&mut Str::new()) diff --git a/crates/nu_plugin_str/src/strutils.rs b/crates/nu_plugin_str/src/strutils.rs index e4b4437f8d..f208580605 100644 --- a/crates/nu_plugin_str/src/strutils.rs +++ b/crates/nu_plugin_str/src/strutils.rs @@ -10,12 +10,14 @@ use std::cmp; #[derive(Debug, Eq, PartialEq)] pub enum Action { + Capitalize, Downcase, Upcase, ToInteger, Substring(usize, usize), Replace(ReplaceAction), ToDateTime(String), + Trim, } #[derive(Debug, Eq, PartialEq)] @@ -38,6 +40,22 @@ impl Str { fn apply(&self, input: &str) -> Result { let applied = match self.action.as_ref() { + Some(Action::Trim) => UntaggedValue::string(input.trim()), + Some(Action::Capitalize) => { + let mut capitalized = String::new(); + + for (idx, character) in input.chars().enumerate() { + let out = if idx == 0 { + character.to_uppercase().to_string() + } else { + character.to_lowercase().to_string() + }; + + capitalized.push_str(&out); + } + + UntaggedValue::string(capitalized) + } Some(Action::Downcase) => UntaggedValue::string(input.to_ascii_lowercase()), Some(Action::Upcase) => UntaggedValue::string(input.to_ascii_uppercase()), Some(Action::Substring(s, e)) => { @@ -101,6 +119,14 @@ impl Str { self.add_action(Action::ToInteger); } + pub fn for_capitalize(&mut self) { + self.add_action(Action::Capitalize); + } + + pub fn for_trim(&mut self) { + self.add_action(Action::Trim); + } + pub fn for_downcase(&mut self) { self.add_action(Action::Downcase); } @@ -151,7 +177,7 @@ impl Str { } pub fn usage() -> &'static str { - "Usage: str field [--downcase|--upcase|--to-int|--substring \"start,end\"|--replace|--find-replace [pattern replacement]]]" + "Usage: str field [--capitalize|--downcase|--upcase|--to-int|--substring \"start,end\"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]" } pub fn strutils(&self, value: Value) -> Result { @@ -216,6 +242,22 @@ pub mod tests { use super::Str; use nu_plugin::test_helpers::value::{int, string}; + #[test] + fn trim() -> Result<(), Box> { + let mut strutils = Str::new(); + strutils.for_trim(); + assert_eq!(strutils.apply("andres ")?, string("andres").value); + Ok(()) + } + + #[test] + fn capitalize() -> Result<(), Box> { + let mut strutils = Str::new(); + strutils.for_capitalize(); + assert_eq!(strutils.apply("andres")?, string("Andres").value); + Ok(()) + } + #[test] fn downcases() -> Result<(), Box> { let mut strutils = Str::new(); diff --git a/docs/commands/str.md b/docs/commands/str.md index d1ed3edb3d..fd183931e8 100644 --- a/docs/commands/str.md +++ b/docs/commands/str.md @@ -47,4 +47,18 @@ Consumes either a single value or a table and converts the provided data to a st ───────── 6 ━━━━━━━━━ -``` + +> echo "nu" | str --capitalize +━━━━━━━━━ + +───────── + Nu +━━━━━━━━━ + +> echo "Nu " | str --trim +━━━━━━━━━ + +───────── + Nu +━━━━━━━━━ +``` \ No newline at end of file diff --git a/tests/plugins/core_str.rs b/tests/plugins/core_str.rs index 800f4d0fc5..d2f69563e1 100644 --- a/tests/plugins/core_str.rs +++ b/tests/plugins/core_str.rs @@ -9,7 +9,7 @@ fn can_only_apply_one() { "open caco3_plastics.csv | first 1 | str origin --downcase --upcase" ); - assert!(actual.contains(r#"--downcase|--upcase|--to-int|--substring "start,end"|--replace|--find-replace [pattern replacement]]"#)); + assert!(actual.contains(r#"--capitalize|--downcase|--upcase|--to-int|--substring "start,end"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]"#)); } #[test] @@ -34,8 +34,48 @@ fn acts_without_passing_field() { } #[test] -fn downcases() { +fn trims() { Playground::setup("plugin_str_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "nu " + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str dependency.name --trim | get dependency.name | echo $it" + ); + + assert_eq!(actual, "nu"); + }) +} + +#[test] +fn capitalizes() { + Playground::setup("plugin_str_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str dependency.name --capitalize | get dependency.name | echo $it" + ); + + assert_eq!(actual, "Nu"); + }) +} + +#[test] +fn downcases() { + Playground::setup("plugin_str_test_4", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -55,7 +95,7 @@ fn downcases() { #[test] fn upcases() { - Playground::setup("plugin_str_test_3", |dirs, sandbox| { + Playground::setup("plugin_str_test_5", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -93,7 +133,7 @@ fn converts_to_int() { #[test] fn replaces() { - Playground::setup("plugin_str_test_4", |dirs, sandbox| { + Playground::setup("plugin_str_test_5", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -118,7 +158,7 @@ fn replaces() { #[test] fn find_and_replaces() { - Playground::setup("plugin_str_test_5", |dirs, sandbox| { + Playground::setup("plugin_str_test_6", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -143,7 +183,7 @@ fn find_and_replaces() { #[test] fn find_and_replaces_without_passing_field() { - Playground::setup("plugin_str_test_6", |dirs, sandbox| { + Playground::setup("plugin_str_test_7", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#"