From ab666c170c181e054e54679be05c5066d4f1da33 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 18 Mar 2021 13:42:39 -0500 Subject: [PATCH] added the ability to create multi-byte unicode chars like emoji (#3195) --- crates/nu-command/src/commands/char_.rs | 72 ++++++++++++++++++++----- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/crates/nu-command/src/commands/char_.rs b/crates/nu-command/src/commands/char_.rs index b93236291b..965068c9e3 100644 --- a/crates/nu-command/src/commands/char_.rs +++ b/crates/nu-command/src/commands/char_.rs @@ -9,6 +9,7 @@ pub struct Char; #[derive(Deserialize)] struct CharArgs { name: Tagged, + rest: Vec>, unicode: bool, } @@ -25,6 +26,7 @@ impl WholeStreamCommand for Char { SyntaxShape::Any, "the name of the character to output", ) + .rest(SyntaxShape::String, "multiple unicode bytes") .switch("unicode", "unicode string i.e. 1f378", Some('u')) } @@ -53,24 +55,60 @@ impl WholeStreamCommand for Char { example: r#"char -u 1f378"#, result: Some(vec![Value::from("\u{1f378}")]), }, + Example { + description: "Output multi-byte unicode character", + example: r#"char -u 1F468 200D 1F466 200D 1F466"#, + result: Some(vec![Value::from( + "\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}", + )]), + }, ] } async fn run(&self, args: CommandArgs) -> Result { - let (CharArgs { name, unicode }, _) = args.process().await?; + let ( + CharArgs { + name, + rest, + unicode, + }, + _, + ) = args.process().await?; if unicode { - let decoded_char = string_to_unicode_char(&name.item); - if let Some(output) = decoded_char { + if !rest.is_empty() { + // Setup a new buffer to put all the unicode bytes in + let mut multi_byte = String::new(); + // Get the first byte + let decoded_char = string_to_unicode_char(&name.item, &name.tag); + match decoded_char { + Ok(ch) => multi_byte.push(ch), + Err(e) => return Err(e), + } + // Get the rest of the bytes + for byte_part in rest { + let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag); + match decoded_char { + Ok(ch) => multi_byte.push(ch), + Err(e) => return Err(e), + } + } Ok(OutputStream::one(ReturnSuccess::value( - UntaggedValue::string(output).into_value(name.tag()), + UntaggedValue::string(multi_byte).into_value(name.tag), ))) } else { - Err(ShellError::labeled_error( - "error decoding unicode character", - "error decoding unicode character", - name.tag(), - )) + let decoded_char = string_to_unicode_char(&name.item, &name.tag); + if let Ok(ch) = decoded_char { + Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::string(ch).into_value(name.tag()), + ))) + } else { + Err(ShellError::labeled_error( + "error decoding unicode character", + "error decoding unicode character", + name.tag(), + )) + } } } else { let special_character = str_to_character(&name.item); @@ -89,10 +127,20 @@ impl WholeStreamCommand for Char { } } -fn string_to_unicode_char(s: &str) -> Option { - u32::from_str_radix(s, 16) +fn string_to_unicode_char(s: &str, t: &Tag) -> Result { + let decoded_char = u32::from_str_radix(s, 16) .ok() - .and_then(std::char::from_u32) + .and_then(std::char::from_u32); + + if let Some(ch) = decoded_char { + Ok(ch) + } else { + Err(ShellError::labeled_error( + "error decoding unicode character", + "error decoding unicode character", + t, + )) + } } fn str_to_character(s: &str) -> Option {