Skip to main content

nix_compat/aterm/
escape.rs

1use std::sync::LazyLock;
2
3use aho_corasick::AhoCorasick;
4
5const PATTERNS: [&str; 5] = ["\\", "\n", "\r", "\t", "\""];
6const REPLACEMENTS: [&str; 5] = ["\\\\", "\\n", "\\r", "\\t", "\\\""];
7static AC: LazyLock<AhoCorasick> = LazyLock::new(|| {
8    AhoCorasick::builder()
9        .build(PATTERNS)
10        .expect("to init aho-corasick with PATTERNS")
11});
12
13/// Given a byte sequence, writes it in escaped form to the passed writer.
14/// Does not add surrounding quotes.
15pub fn write_escaped<P: AsRef<[u8]>>(s: P, w: &mut impl std::io::Write) -> std::io::Result<()> {
16    AC.try_stream_replace_all(s.as_ref(), w, &REPLACEMENTS)
17}
18
19#[cfg(test)]
20mod tests {
21    use super::write_escaped;
22    use rstest::rstest;
23
24    #[rstest]
25    #[case::empty(b"", b"")]
26    #[case::doublequote(b"\"", b"\\\"")]
27    #[case::colon(b":", b":")]
28    #[case::complex(b"foo\n\rbar\\baz", b"foo\\n\\rbar\\\\baz")]
29    fn escape(#[case] input: &[u8], #[case] expected: &[u8]) {
30        let mut buf = Vec::new();
31        write_escaped(input, &mut buf).unwrap();
32
33        assert_eq!(expected, buf.as_slice());
34    }
35}