1use std::{fmt::Formatter, ops::Deref};
2
3use serde::{de::Error, Deserialize, Deserializer};
4
5#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub struct EmojiString(String);
12
13impl Deref for EmojiString {
14 type Target = str;
15
16 fn deref(&self) -> &Self::Target {
17 &self.0
18 }
19}
20
21impl From<&str> for EmojiString {
22 fn from(mut value: &str) -> Self {
23 let mut o = String::new();
24
25 while let Some((i, m, n, j)) = value
36 .find(':')
37 .map(|i| (i, i + 1))
38 .and_then(|(i, m)| value[m..].find(':').map(|x| (i, m, m + x, m + x + 1)))
39 {
40 if let Some(emoji) = emojis::get_by_shortcode(&value[m..n]) {
41 o.push_str(&value[..i]);
43 o.push_str(emoji.as_str());
45 value = &value[j..];
47 } else {
48 o.push_str(&value[..n]);
50 value = &value[n..];
52 }
53 }
54
55 o.push_str(value);
56 Self(o)
57 }
58}
59
60impl From<EmojiString> for String {
61 fn from(value: EmojiString) -> Self {
62 value.0
63 }
64}
65
66impl<'de> Deserialize<'de> for EmojiString {
67 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68 where
69 D: Deserializer<'de>,
70 {
71 struct Visitor;
72
73 impl<'de> serde::de::Visitor<'de> for Visitor {
74 type Value = EmojiString;
75
76 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
77 formatter.write_str("a string containing emoji shortcodes")
78 }
79
80 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
81 where
82 E: Error,
83 {
84 Ok(v.into())
85 }
86
87 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
88 where
89 E: Error,
90 {
91 let s = std::str::from_utf8(v)
92 .map_err(|_| E::invalid_value(serde::de::Unexpected::Bytes(v), &self))?;
93 Ok(s.into())
94 }
95 }
96
97 deserializer.deserialize_str(Visitor)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 pub fn test_from_str() {
107 let tests = [
108 ("launch nothing", "launch nothing"),
109 ("launch :rocket: something", "launch 🚀 something"),
110 ("? :unknown: emoji", "? :unknown: emoji"),
111 ("::very:naughty::", "::very:naughty::"),
112 (":maybe:rocket:", ":maybe🚀"),
113 (":rocket::rocket:", "🚀🚀"),
114 ];
115
116 for (i, o) in tests {
117 let i: EmojiString = i.into();
118 assert_eq!(&*i, o);
119 }
120 }
121
122 #[test]
123 pub fn test_deserialize() {
124 let tests = [
125 ("launch nothing", "launch nothing"),
126 ("launch :rocket: something", "launch 🚀 something"),
127 ("? :unknown: emoji", "? :unknown: emoji"),
128 ("::very:naughty::", "::very:naughty::"),
129 (":maybe:rocket:", ":maybe🚀"),
130 (":rocket::rocket:", "🚀🚀"),
131 ];
132
133 for (i, o) in tests {
134 let i: EmojiString = serde_json::from_str(&format!("\"{i}\"")).unwrap();
135 assert_eq!(&*i, o);
136 }
137 }
138}