From 2129ec7558304a5ff0a40d180b7b4de63be9ad9d Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 26 Jan 2021 03:09:38 -0600 Subject: [PATCH] allow pad to use multi-byte chars (#2973) --- crates/nu-command/src/commands/str_/lpad.rs | 27 +++++++++++++++------ crates/nu-command/src/commands/str_/rpad.rs | 27 +++++++++++++++------ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/crates/nu-command/src/commands/str_/lpad.rs b/crates/nu-command/src/commands/str_/lpad.rs index a12f2c1df..da28766b5 100644 --- a/crates/nu-command/src/commands/str_/lpad.rs +++ b/crates/nu-command/src/commands/str_/lpad.rs @@ -11,7 +11,7 @@ use nu_value_ext::ValueExt; #[derive(Deserialize)] struct Arguments { length: Tagged, - character: Tagged, + character: Tagged, rest: Vec, } @@ -67,6 +67,13 @@ impl WholeStreamCommand for SubCommand { example: "echo '123456789' | str lpad -l 3 -c '0'", result: Some(vec![UntaggedValue::string("123").into_untagged_value()]), }, + Example { + description: "Use lpad to pad unicode", + example: "echo '▉' | str lpad -l 10 -c '▉'", + result: Some(vec![ + UntaggedValue::string("▉▉▉▉▉▉▉▉▉▉").into_untagged_value() + ]), + }, ] } } @@ -85,16 +92,17 @@ async fn operate(args: CommandArgs) -> Result { Ok(input .map(move |v| { let len = length.item; - let character = character.item; + let character = character.item.clone(); if column_paths.is_empty() { ReturnSuccess::value(action(&v, len, character, v.tag())?) } else { let mut ret = v; for path in &column_paths { + let str_clone = character.clone(); ret = ret.swap_data_by_column_path( path, - Box::new(move |old| action(old, len, character, old.tag())), + Box::new(move |old| action(old, len, str_clone, old.tag())), )?; } @@ -107,15 +115,18 @@ async fn operate(args: CommandArgs) -> Result { fn action( input: &Value, length: usize, - character: char, + character: String, tag: impl Into, ) -> Result { match &input.value { UntaggedValue::Primitive(Primitive::String(s)) => { if length < s.len() { - Ok(UntaggedValue::string(&s[0..length]).into_value(tag)) + Ok( + UntaggedValue::string(s.chars().take(length).collect::()) + .into_value(tag), + ) } else { - let mut res = character.to_string().repeat(length - s.len()); + let mut res = character.repeat(length - s.chars().count()); res += s.as_ref(); Ok(UntaggedValue::string(res).into_value(tag)) } @@ -149,7 +160,7 @@ mod tests { #[test] fn left_pad_with_zeros() { let word = string("123"); - let pad_char = '0'; + let pad_char = '0'.to_string(); let pad_len = 10; let expected = UntaggedValue::string("0000000123").into_untagged_value(); @@ -160,7 +171,7 @@ mod tests { #[test] fn left_pad_but_truncate() { let word = string("123456789"); - let pad_char = '0'; + let pad_char = '0'.to_string(); let pad_len = 3; let expected = UntaggedValue::string("123").into_untagged_value(); diff --git a/crates/nu-command/src/commands/str_/rpad.rs b/crates/nu-command/src/commands/str_/rpad.rs index 1ded3116a..1e7a96b3b 100644 --- a/crates/nu-command/src/commands/str_/rpad.rs +++ b/crates/nu-command/src/commands/str_/rpad.rs @@ -11,7 +11,7 @@ use nu_value_ext::ValueExt; #[derive(Deserialize)] struct Arguments { length: Tagged, - character: Tagged, + character: Tagged, rest: Vec, } @@ -67,6 +67,13 @@ impl WholeStreamCommand for SubCommand { example: "echo '123456789' | str rpad -l 3 -c '0'", result: Some(vec![UntaggedValue::string("123").into_untagged_value()]), }, + Example { + description: "Use rpad to pad unicode", + example: "echo '▉' | str rpad -l 10 -c '▉'", + result: Some(vec![ + UntaggedValue::string("▉▉▉▉▉▉▉▉▉▉").into_untagged_value() + ]), + }, ] } } @@ -85,16 +92,17 @@ async fn operate(args: CommandArgs) -> Result { Ok(input .map(move |v| { let len = length.item; - let character = character.item; + let character = character.item.clone(); if column_paths.is_empty() { ReturnSuccess::value(action(&v, len, character, v.tag())?) } else { let mut ret = v; for path in &column_paths { + let str_clone = character.clone(); ret = ret.swap_data_by_column_path( path, - Box::new(move |old| action(old, len, character, old.tag())), + Box::new(move |old| action(old, len, str_clone, old.tag())), )?; } @@ -107,16 +115,19 @@ async fn operate(args: CommandArgs) -> Result { fn action( input: &Value, length: usize, - character: char, + character: String, tag: impl Into, ) -> Result { match &input.value { UntaggedValue::Primitive(Primitive::String(s)) => { if length < s.len() { - Ok(UntaggedValue::string(&s[0..length]).into_value(tag)) + Ok( + UntaggedValue::string(s.chars().take(length).collect::()) + .into_value(tag), + ) } else { let mut res = s.to_string(); - res += character.to_string().repeat(length - s.len()).as_str(); + res += character.repeat(length - s.chars().count()).as_str(); Ok(UntaggedValue::string(res).into_value(tag)) } } @@ -149,7 +160,7 @@ mod tests { #[test] fn right_pad_with_zeros() { let word = string("123"); - let pad_char = '0'; + let pad_char = '0'.to_string(); let pad_len = 10; let expected = UntaggedValue::string("1230000000").into_untagged_value(); @@ -160,7 +171,7 @@ mod tests { #[test] fn right_pad_but_truncate() { let word = string("123456789"); - let pad_char = '0'; + let pad_char = '0'.to_string(); let pad_len = 3; let expected = UntaggedValue::string("123").into_untagged_value();