1use alloc::{borrow::Cow, string::String, vec::Vec};
2
3pub 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
22pub 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
41pub struct Escaped<W>(pub W);
43
44impl<W> Escaped<W> {
45 pub fn new(write: W) -> Self {
47 Self(write)
48 }
49
50 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}