added the ability to create multi-byte unicode chars like emoji (#3195)

This commit is contained in:
Darren Schroeder 2021-03-18 13:42:39 -05:00 committed by GitHub
parent d2213d18fa
commit ab666c170c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -9,6 +9,7 @@ pub struct Char;
#[derive(Deserialize)] #[derive(Deserialize)]
struct CharArgs { struct CharArgs {
name: Tagged<String>, name: Tagged<String>,
rest: Vec<Tagged<String>>,
unicode: bool, unicode: bool,
} }
@ -25,6 +26,7 @@ impl WholeStreamCommand for Char {
SyntaxShape::Any, SyntaxShape::Any,
"the name of the character to output", "the name of the character to output",
) )
.rest(SyntaxShape::String, "multiple unicode bytes")
.switch("unicode", "unicode string i.e. 1f378", Some('u')) .switch("unicode", "unicode string i.e. 1f378", Some('u'))
} }
@ -53,24 +55,60 @@ impl WholeStreamCommand for Char {
example: r#"char -u 1f378"#, example: r#"char -u 1f378"#,
result: Some(vec![Value::from("\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<OutputStream, ShellError> { async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (CharArgs { name, unicode }, _) = args.process().await?; let (
CharArgs {
name,
rest,
unicode,
},
_,
) = args.process().await?;
if unicode { if unicode {
let decoded_char = string_to_unicode_char(&name.item); if !rest.is_empty() {
if let Some(output) = decoded_char { // 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( Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()), UntaggedValue::string(multi_byte).into_value(name.tag),
))) )))
} else { } else {
Err(ShellError::labeled_error( let decoded_char = string_to_unicode_char(&name.item, &name.tag);
"error decoding unicode character", if let Ok(ch) = decoded_char {
"error decoding unicode character", Ok(OutputStream::one(ReturnSuccess::value(
name.tag(), UntaggedValue::string(ch).into_value(name.tag()),
)) )))
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
name.tag(),
))
}
} }
} else { } else {
let special_character = str_to_character(&name.item); let special_character = str_to_character(&name.item);
@ -89,10 +127,20 @@ impl WholeStreamCommand for Char {
} }
} }
fn string_to_unicode_char(s: &str) -> Option<char> { fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
u32::from_str_radix(s, 16) let decoded_char = u32::from_str_radix(s, 16)
.ok() .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<String> { fn str_to_character(s: &str) -> Option<String> {