Skip to main content

i18n_string/
escape.rs

1use alloc::{borrow::Cow, string::String, vec::Vec};
2
3/// Escape special characters in a string to fit I18nString format.
4pub fn escape(input: &str) -> Cow<'_, str> {
5    if !input.contains(['\'', '\\', '\n', '\t']) {
6        return Cow::Borrowed(input);
7    }
8
9    let mut output = String::with_capacity(input.len());
10    for c in input.chars() {
11        match c {
12            '\'' => output.push_str("\\'"),
13            '\\' => output.push_str("\\\\"),
14            '\n' => output.push_str("\\n"),
15            '\t' => output.push_str("\\t"),
16            _ => output.push(c),
17        }
18    }
19    Cow::Owned(output)
20}
21
22/// Escape special characters in a byte slice to fit I18nString format.
23pub fn escape_bytes(input: &[u8]) -> Cow<'_, [u8]> {
24    if !input.iter().any(|&c| c == b'\'' || c == b'\\' || c == b'\n' || c == b'\t') {
25        return Cow::Borrowed(input);
26    }
27
28    let mut output = Vec::with_capacity(input.len());
29    for &c in input {
30        match c {
31            b'\'' => output.extend_from_slice(b"\\'"),
32            b'\\' => output.extend_from_slice(b"\\\\"),
33            b'\n' => output.extend_from_slice(b"\\n"),
34            b'\t' => output.extend_from_slice(b"\\t"),
35            _ => output.push(c),
36        }
37    }
38    Cow::Owned(output)
39}
40
41/// A wrapper for a formatter that escapes special characters in I18nString format.
42pub struct Escaped<W>(pub W);
43
44impl<W> Escaped<W> {
45    /// Create a new `Escaped` formatter.
46    pub fn new(write: W) -> Self {
47        Self(write)
48    }
49
50    /// Get the inner formatter.
51    pub fn into_inner(self) -> W {
52        self.0
53    }
54}
55
56impl<W: core::fmt::Write> core::fmt::Write for Escaped<W> {
57    fn write_str(&mut self, s: &str) -> core::fmt::Result {
58        self.0.write_str(escape(s).as_ref())
59    }
60
61    fn write_char(&mut self, c: char) -> core::fmt::Result {
62        match c {
63            '\'' => self.0.write_str("\\'"),
64            '\\' => self.0.write_str("\\\\"),
65            '\n' => self.0.write_str("\\n"),
66            '\t' => self.0.write_str("\\t"),
67            _ => self.0.write_char(c),
68        }
69    }
70}
71
72#[cfg(feature = "std")]
73impl<W: std::io::Write> std::io::Write for Escaped<W> {
74    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
75        self.0.write(escape_bytes(buf).as_ref())
76    }
77
78    fn flush(&mut self) -> std::io::Result<()> {
79        self.0.flush()
80    }
81}