diff --git a/Cargo.lock b/Cargo.lock index 290039d4..4b4fb944 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,6 +638,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "emojis" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1f1df1f181f2539bac8bf027d31ca5ffbf9e559e3f2d09413b9107b5c02f4" +dependencies = [ + "phf", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1709,6 +1718,24 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -1877,6 +1904,7 @@ dependencies = [ "colored", "crossbeam-channel", "cyphernet", + "emojis", "fastrand", "git2", "libc", diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml index fbc5db7c..74853a1b 100644 --- a/radicle/Cargo.toml +++ b/radicle/Cargo.toml @@ -69,6 +69,7 @@ default-features = false optional = true [dev-dependencies] +emojis = { version = "0.6" } pretty_assertions = { version = "1.3.0" } qcheck-macros = { version = "1", default-features = false } qcheck = { version = "1", default-features = false } diff --git a/radicle/src/cob/common.rs b/radicle/src/cob/common.rs index 9c7eec68..bdc94d30 100644 --- a/radicle/src/cob/common.rs +++ b/radicle/src/cob/common.rs @@ -91,12 +91,18 @@ impl Reaction { pub fn new(emoji: char) -> Result { let val = emoji as u32; let emoticons = 0x1F600..=0x1F64F; + let hearts = 0x1FA75..=0x1FA77; + let body_update = 0x1FAC0..=0x1FAC6; + let emoticons_update = 0x1FAE0..=0x1FAF8; let misc = 0x1F300..=0x1F5FF; // Miscellaneous Symbols and Pictographs let dingbats = 0x2700..=0x27BF; let supp = 0x1F900..=0x1F9FF; // Supplemental Symbols and Pictographs let transport = 0x1F680..=0x1F6FF; if emoticons.contains(&val) + || hearts.contains(&val) + || body_update.contains(&val) + || emoticons_update.contains(&val) || misc.contains(&val) || dingbats.contains(&val) || supp.contains(&val) @@ -367,32 +373,6 @@ impl From for Authorization { } } -#[cfg(test)] -#[allow(clippy::unwrap_used)] -mod test { - use super::*; - - #[test] - fn test_color() { - let c = Color::from_str("#ffccaa").unwrap(); - assert_eq!(c.to_string(), "#ffccaa".to_owned()); - assert_eq!(serde_json::to_string(&c).unwrap(), "\"#ffccaa\"".to_owned()); - assert_eq!(serde_json::from_str::<'_, Color>("\"#ffccaa\"").unwrap(), c); - - let c = Color::from_str("#0000aa").unwrap(); - assert_eq!(c.to_string(), "#0000aa".to_owned()); - - let c = Color::from_str("#aa0000").unwrap(); - assert_eq!(c.to_string(), "#aa0000".to_owned()); - - let c = Color::from_str("#00aa00").unwrap(); - assert_eq!(c.to_string(), "#00aa00".to_owned()); - - Color::from_str("#aa00").unwrap_err(); - Color::from_str("#abc").unwrap_err(); - } -} - /// Describes a code location that can be used for comments on /// patches, issues, and diffs. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -446,3 +426,60 @@ impl std::cmp::Ord for CodeRange { } } } + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod test { + use std::collections::BTreeSet; + + use super::*; + + #[test] + fn test_color() { + let c = Color::from_str("#ffccaa").unwrap(); + assert_eq!(c.to_string(), "#ffccaa".to_owned()); + assert_eq!(serde_json::to_string(&c).unwrap(), "\"#ffccaa\"".to_owned()); + assert_eq!(serde_json::from_str::<'_, Color>("\"#ffccaa\"").unwrap(), c); + + let c = Color::from_str("#0000aa").unwrap(); + assert_eq!(c.to_string(), "#0000aa".to_owned()); + + let c = Color::from_str("#aa0000").unwrap(); + assert_eq!(c.to_string(), "#aa0000".to_owned()); + + let c = Color::from_str("#00aa00").unwrap(); + assert_eq!(c.to_string(), "#00aa00".to_owned()); + + Color::from_str("#aa00").unwrap_err(); + Color::from_str("#abc").unwrap_err(); + } + + #[test] + fn test_emojis() { + let emojis = emojis::Group::SmileysAndEmotion + .emojis() + .chain(emojis::Group::PeopleAndBody.emojis()) + .filter_map(|emoji| { + if emoji.as_str().chars().count() == 1 { + Some(emoji.as_str().chars().next().unwrap()) + } else { + None + } + }); + let mut failed = BTreeSet::new(); + for emoji in emojis { + if Reaction::new(emoji).is_err() { + failed.insert(emoji); + } + } + if !failed.is_empty() { + for emoji in failed { + eprintln!( + "cannot construct Reaction for '{emoji}' {:#04X}", + (emoji as u32) + ); + } + panic!("Failed to construct Reaction for these emojis"); + } + } +}