1use crate::{Binary, Error};
2use std::str::FromStr;
3
4pub const ZERO_WIDTH_SPACE: (char, &str) = ('\u{200B}', "​");
30
31pub const ZERO_WIDTH_NON_JOINER: (char, &str) = ('\u{200C}', "‌");
47
48pub const ZERO_WIDTH_JOINER: (char, &str) = ('\u{200D}', "‍");
64
65pub const WORD_JOINER: (char, &str) = ('\u{2060}', "⁠");
79
80pub const ZERO_WIDTH_NO_BREAK_SPACE: (char, &str) = ('\u{FEFF}', "");
96
97pub struct ZeroWidth(Binary);
98
99impl From<Binary> for ZeroWidth {
100 fn from(binary: Binary) -> Self {
101 Self(binary)
102 }
103}
104
105impl ZeroWidth {
106 pub fn new(string: &str) -> Result<Self, Error> {
108 let binary = Binary::from_str(string)?;
109
110 Ok(ZeroWidth::from(binary))
111 }
112
113 pub fn get_binary_string(&self) -> String {
116 self.0.to_string()
117 }
118
119 pub fn to_unicode(&self) -> String {
122 let string_value = self.get_binary_string();
123 let mut zero_width = String::default();
124
125 let chars: Vec<&str> = string_value.split(' ').collect();
127
128 chars.into_iter().for_each(|character| {
129 character.split("").for_each(|ch| {
130 if ch.eq("0") {
131 zero_width.push(ZERO_WIDTH_SPACE.0);
132 } else {
133 zero_width.push(ZERO_WIDTH_NON_JOINER.0);
134 }
135 });
136
137 zero_width.push(ZERO_WIDTH_JOINER.0);
138 });
139
140 zero_width.pop();
142
143 zero_width
144 }
145
146 pub fn to_html(&self) -> String {
149 let string_value = self.get_binary_string();
150 let mut zero_width = String::default();
151
152 let chars: Vec<&str> = string_value.split(' ').collect();
154
155 chars.into_iter().for_each(|character| {
156 character.split("").for_each(|ch| {
157 if ch.eq("0") {
158 zero_width.push_str(ZERO_WIDTH_SPACE.1);
159 } else {
160 zero_width.push_str(ZERO_WIDTH_NON_JOINER.1);
161 }
162 });
163
164 zero_width.push_str(ZERO_WIDTH_JOINER.1);
165 });
166
167 zero_width.pop();
169
170 zero_width
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use super::*;
177 const RUSTACEANS_ZW_UNICODE: &str = "\u{200c}\u{200b}\u{200c}\u{200b}\u{200c}\u{200b}\u{200b}\u{200c}\u{200b}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200c}\u{200b}\u{200c}\u{200b}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200c}\u{200b}\u{200b}\u{200c}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200c}\u{200b}\u{200c}\u{200b}\u{200b}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200b}\u{200b}\u{200b}\u{200b}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200b}\u{200b}\u{200b}\u{200c}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200b}\u{200b}\u{200c}\u{200b}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200b}\u{200b}\u{200b}\u{200b}\u{200c}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200b}\u{200c}\u{200c}\u{200c}\u{200b}\u{200c}\u{200d}\u{200c}\u{200b}\u{200c}\u{200c}\u{200c}\u{200b}\u{200b}\u{200c}\u{200c}\u{200c}";
178 const RUSTACEANS_ZW_HTML: &str = "‌​‌​‌​​‌​‌‍‌​‌‌‌​‌​‌‌‍‌​‌‌‌​​‌‌‌‍‌​‌‌‌​‌​​‌‍‌​‌‌​​​​‌‌‍‌​‌‌​​​‌‌‌‍‌​‌‌​​‌​‌‌‍‌​‌‌​​​​‌‌‍‌​‌‌​‌‌‌​‌‍‌​‌‌‌​​‌‌‌‍";
179
180 #[test]
181 fn it_zw_into_unicode() {
182 let have = ZeroWidth::new("Rustaceans").unwrap().to_unicode();
183 let want = RUSTACEANS_ZW_UNICODE.to_string();
184
185 assert_eq!(have, want);
186 }
187
188 #[test]
189 fn it_zw_into_html() {
190 let have = ZeroWidth::new("Rustaceans").unwrap().to_html();
191 let want = RUSTACEANS_ZW_HTML.to_string();
192
193 assert_eq!(have, want);
194 }
195}