tui_markup/generator/helper/
unescape.rs1#[must_use]
17pub fn unescape(escaped: &str) -> Unescape {
18 let cursor = escaped.starts_with('\\').into();
19 Unescape { escaped, cursor }
20}
21
22#[derive(Debug)]
24pub struct Unescape<'a> {
25 escaped: &'a str,
26 cursor: usize,
27}
28
29impl<'a> Iterator for Unescape<'a> {
30 type Item = &'a str;
31
32 fn next(&mut self) -> Option<Self::Item> {
33 if self.cursor >= self.escaped.len() {
34 return None;
35 }
36
37 let start = self.cursor + 1;
38 let end = if start >= self.escaped.len() {
39 self.escaped.len()
40 } else {
41 self.escaped[start..]
42 .find('\\')
43 .map_or(self.escaped.len(), |i| start + i)
44 };
45
46 let result = Some(&self.escaped[self.cursor..end]);
47 self.cursor = end + 1;
48
49 result
50 }
51}
52
53#[cfg(test)]
54mod test {
55 macro_rules! test_unescape {
56 ($escaped:expr => $($result:expr),* $(,)?) => {
57 assert_eq!(crate::generator::helper::unescape($escaped).collect::<Vec<_>>(), vec![$($result),+])
58 };
59 }
60
61 #[test]
62 fn test_escaped_string_at_begin() {
63 test_unescape!("\\<b" => "<b");
64 test_unescape!("\\>b" => ">b");
65 test_unescape!("\\\\b" => "\\b");
66 }
67
68 #[test]
69 fn test_escaped_string_at_middle() {
70 test_unescape!("a\\<b" => "a", "<b");
71 test_unescape!("a\\>b" => "a", ">b");
72 test_unescape!("a\\\\b" => "a", "\\b");
73 }
74
75 #[test]
76 fn test_escaped_string_at_end() {
77 test_unescape!("a\\<" => "a", "<");
78 test_unescape!("a\\>" => "a", ">");
79 test_unescape!("a\\\\" => "a", "\\");
80 }
81
82 #[test]
83 fn test_escaped_string_multi() {
84 test_unescape!("1\\<2\\<3 \\\\ 3\\>2\\>1" => "1", "<2", "<3 ", "\\ 3", ">2", ">1");
85 }
86}