protokit_textformat 0.2.0

Usable protocol buffers
Documentation
use core::iter::Peekable;

pub fn escape_bytes_to(bytes: &[u8], buf: &mut String) {
    for c in bytes {
        match c {
            b'\n' => buf.push_str(r"\n"),
            b'\r' => buf.push_str(r"\r"),
            b'\t' => buf.push_str(r"\t"),
            b'\'' => buf.push_str("\\\'"),
            b'"' => buf.push_str("\\\""),
            b'\\' => buf.push_str("\\\\"),

            7 => buf.push_str("\\a"),
            8 => buf.push_str("\\b"),

            11 => buf.push_str("\\v"),
            12 => buf.push_str("\\f"),

            63 => buf.push_str("\\?"),

            b'\x20' ..= b'\x7e' => buf.push(*c as char),
            _ => {
                buf.push('\\');
                buf.push((b'0' + (c >> 6)) as char);
                buf.push((b'0' + ((c >> 3) & 7)) as char);
                buf.push((b'0' + (c & 7)) as char);
            }
        }
    }
}

pub fn unescape<I: Iterator<Item = u8>>(it: I) -> Unescape<I> {
    Unescape {
        it: it.peekable(),
        hleft: 0,
    }
}

pub struct Unescape<I: Iterator<Item = u8>> {
    it: Peekable<I>,
    hleft: u8,
}

fn fhex(c: u8) -> u8 {
    (c & 0x0F) + 9 - ((c & 0x10) >> 1) - ((c & 0x10) >> 4)
}

#[test]
fn test_hex() {
    assert_eq!(fhex(b'a'), 0xa);
    assert_eq!(fhex(b'A'), 0xa);
}

impl<I: Iterator<Item = u8>> Iterator for Unescape<I> {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if self.hleft > 0 {
                let a = self.it.next()?;
                let b = self.it.next()?;
                self.hleft -= 1;
                return Some(fhex(a) << 4 | fhex(b));
            }

            let bslash = self.it.next()?;
            if bslash != b'\\' {
                return Some(bslash);
            }
            let next = match self.it.next()? {
                b'a' => 7,
                b'b' => 8,
                b't' => 9,
                b'n' => 10,
                b'v' => 11,
                b'f' => 12,
                b'r' => 13,
                b'"' => 34,
                b'\'' => 39,
                b'?' => 63,
                b'\\' => 92,
                b'x' => {
                    self.hleft = 1;
                    continue;
                }
                b'u' => {
                    self.hleft = 2;
                    continue;
                }
                b'U' => {
                    let _ = self.it.next();
                    let _ = self.it.next();
                    self.hleft = 3;
                    continue;
                }
                first @ b'0' ..= b'7' => {
                    let mut value = first - b'0';
                    for _ in 0 .. 2 {
                        match self.it.peek() {
                            Some(x @ b'0' ..= b'7') => {
                                value = value << 3 | (*x - b'0');
                                let _ = self.it.next();
                            }
                            _ => break,
                        }
                    }
                    value
                }
                _ => panic!("Invalid escape"),
            };
            return Some(next);
        }
    }
}