flams_utils/
escaping.rs

1use std::fmt::Display;
2
3pub struct Escaper<C, const N: usize>(pub [(C, &'static str); N]);
4impl<const N: usize> Escaper<char, N> {
5    pub fn escape<'a, D: Display>(&'a self, display: &'a D) -> impl Display + 'a {
6        EscaperI {
7            display,
8            replacements: &self.0,
9        }
10    }
11}
12impl<const N: usize> Escaper<u8, N> {
13    pub fn escape<'a, D: Display>(&'a self, display: &'a D) -> impl Display + 'a {
14        EscaperI {
15            display,
16            replacements: &self.0,
17        }
18    }    
19    pub fn unescape<'a, D: Display>(&'a self, display: &'a D) -> impl Display + 'a {
20        UnEscaperI {
21            display,
22            replacements: &self.0,
23        }
24    }
25}
26
27struct EscaperI<'a, D: Display, C, const N: usize> {
28    display: &'a D,
29    replacements: &'a [(C, &'static str); N],
30}
31impl<D: Display, const N: usize> Display for EscaperI<'_, D, char, N> {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        let mut r = Replacer {
34            writer: f,
35            replacements: self.replacements,
36        };
37        std::fmt::Write::write_fmt(&mut r, format_args!("{}", self.display))
38    }
39}
40impl<D: Display, const N: usize> Display for EscaperI<'_, D, u8, N> {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        let mut r = Replacer {
43            writer: f,
44            replacements: self.replacements,
45        };
46        std::fmt::Write::write_fmt(&mut r, format_args!("{}", self.display))
47    }
48}
49
50struct UnEscaperI<'a, D: Display, C, const N: usize> {
51    display: &'a D,
52    replacements: &'a [(C, &'static str); N],
53}
54impl<D: Display, const N: usize> Display for UnEscaperI<'_, D, u8, N> {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        let mut r = RevReplacer {
57            writer: f,
58            replacements: self.replacements,
59        };
60        std::fmt::Write::write_fmt(&mut r, format_args!("{}", self.display))
61    }
62}
63
64struct Replacer<'a, W: std::fmt::Write, C, const N: usize> {
65    writer: W,
66    replacements: &'a [(C, &'static str); N],
67}
68impl<W: std::fmt::Write, const N: usize> std::fmt::Write for Replacer<'_, W, char, N> {
69    fn write_str(&mut self, s: &str) -> std::fmt::Result {
70        for c in s.chars() {
71            self.write_char(c)?;
72        }
73        Ok(())
74    }
75    fn write_char(&mut self, c: char) -> std::fmt::Result {
76        for (r, s) in self.replacements {
77            if c == *r {
78                return self.writer.write_str(s);
79            }
80        }
81        self.writer.write_char(c)
82    }
83}
84impl<W: std::fmt::Write, const N: usize> std::fmt::Write for Replacer<'_, W, u8, N> {
85    fn write_str(&mut self, s: &str) -> std::fmt::Result {
86        for c in s.as_bytes() {
87            self.write_char(*c as char)?;
88        }
89        Ok(())
90    }
91    fn write_char(&mut self, c: char) -> std::fmt::Result {
92        for (r, s) in self.replacements {
93            if c == *r as char {
94                return self.writer.write_str(s);
95            }
96        }
97        self.writer.write_char(c)
98    }
99}
100
101struct RevReplacer<'a, W: std::fmt::Write, C, const N: usize> {
102    writer: W,
103    replacements: &'a [(C, &'static str); N],
104}
105
106impl<W: std::fmt::Write, const N: usize> std::fmt::Write for RevReplacer<'_, W, u8, N> {
107    fn write_str(&mut self, s: &str) -> std::fmt::Result {
108        let bytes = self.replacements.map(|(a,b)| (a,b.as_bytes()));
109        let mut s = s.as_bytes();
110        'outer: loop {
111            let mut i = 0;
112            while i < s.len() {
113                if let Some((a,b)) = bytes.iter().find_map(|(b,needle)|
114                    s.strip_prefix(*needle).map(|r| (*b,r) )
115                ) {
116                    self.writer.write_str(std::str::from_utf8(&s[..i]).map_err(|_|std::fmt::Error)?)?;
117                    self.writer.write_char(a as char)?;
118                    s = b;
119                    continue 'outer
120                }
121                i += 1;
122            }
123            return self.writer.write_str(std::str::from_utf8(s).map_err(|_| std::fmt::Error)?);
124        }
125    }
126    fn write_char(&mut self, c: char) -> std::fmt::Result {
127        self.writer.write_char(c)
128    }
129}
130
131pub static IRI_ESCAPE: Escaper<u8, 5> = Escaper([
132    (b' ', "%20"),
133    (b'\\', "%5C"),
134    (b'^', "%5E"),
135    (b'[', "%5B"),
136    (b']', "%5D"),
137]);