wav-codec 0.1.0

WAV (RIFF/WAVE) reader and writer over std I/O. PCM and IEEE-float WAV only. Compressed WAV (ADPCM, mu-law, A-law) not decoded.
Documentation
//! Minimal JSON reader for `conformance-vectors.json`, so the test harness stays
//! on std alone.

#[derive(Clone, Debug, PartialEq)]
pub enum Json {
    Null,
    Bool(bool),
    Num(f64),
    Str(String),
    Arr(Vec<Json>),
    Obj(Vec<(String, Json)>),
}

impl Json {
    pub fn get(&self, key: &str) -> Option<&Json> {
        match self {
            Json::Obj(fields) => fields.iter().find(|(k, _)| k == key).map(|(_, v)| v),
            _ => None,
        }
    }

    pub fn field(&self, key: &str) -> &Json {
        match self.get(key) {
            Some(v) => v,
            None => panic!("missing JSON field {:?}", key),
        }
    }

    pub fn as_str(&self) -> &str {
        match self {
            Json::Str(s) => s,
            other => panic!("expected string, found {:?}", other),
        }
    }

    pub fn as_f64(&self) -> f64 {
        match self {
            Json::Num(n) => *n,
            other => panic!("expected number, found {:?}", other),
        }
    }

    pub fn as_u64(&self) -> u64 {
        let n = self.as_f64();
        assert!(n >= 0.0 && n.fract() == 0.0, "not a u64: {}", n);
        n as u64
    }

    pub fn as_i64(&self) -> i64 {
        let n = self.as_f64();
        assert!(n.fract() == 0.0, "not an i64: {}", n);
        n as i64
    }

    pub fn as_bool(&self) -> bool {
        match self {
            Json::Bool(b) => *b,
            other => panic!("expected bool, found {:?}", other),
        }
    }

    pub fn as_array(&self) -> &[Json] {
        match self {
            Json::Arr(a) => a,
            other => panic!("expected array, found {:?}", other),
        }
    }
}

pub fn parse(text: &str) -> Json {
    let mut p = Parser {
        bytes: text.as_bytes(),
        pos: 0,
    };
    let value = p.value();
    p.ws();
    assert_eq!(p.pos, p.bytes.len(), "trailing JSON content");
    value
}

struct Parser<'a> {
    bytes: &'a [u8],
    pos: usize,
}

impl<'a> Parser<'a> {
    fn peek(&self) -> u8 {
        self.bytes.get(self.pos).copied().unwrap_or(0)
    }

    fn ws(&mut self) {
        while matches!(self.peek(), b' ' | b'\n' | b'\r' | b'\t') {
            self.pos += 1;
        }
    }

    fn expect(&mut self, b: u8) {
        self.ws();
        assert_eq!(
            self.peek(),
            b,
            "expected {:?} at byte {}",
            b as char,
            self.pos
        );
        self.pos += 1;
    }

    fn value(&mut self) -> Json {
        self.ws();
        match self.peek() {
            b'{' => self.object(),
            b'[' => self.array(),
            b'"' => Json::Str(self.string()),
            b't' => self.literal("true", Json::Bool(true)),
            b'f' => self.literal("false", Json::Bool(false)),
            b'n' => self.literal("null", Json::Null),
            _ => self.number(),
        }
    }

    fn literal(&mut self, word: &str, value: Json) -> Json {
        let end = self.pos + word.len();
        assert_eq!(&self.bytes[self.pos..end], word.as_bytes());
        self.pos = end;
        value
    }

    fn number(&mut self) -> Json {
        let start = self.pos;
        while matches!(self.peek(), b'-' | b'+' | b'.' | b'e' | b'E' | b'0'..=b'9') {
            self.pos += 1;
        }
        let text = std::str::from_utf8(&self.bytes[start..self.pos]).unwrap();
        Json::Num(
            text.parse()
                .unwrap_or_else(|_| panic!("bad number {:?}", text)),
        )
    }

    fn string(&mut self) -> String {
        self.expect(b'"');
        let mut out = String::new();
        loop {
            let b = self.peek();
            self.pos += 1;
            match b {
                b'"' => return out,
                b'\\' => {
                    let esc = self.peek();
                    self.pos += 1;
                    match esc {
                        b'"' => out.push('"'),
                        b'\\' => out.push('\\'),
                        b'/' => out.push('/'),
                        b'n' => out.push('\n'),
                        b't' => out.push('\t'),
                        b'r' => out.push('\r'),
                        b'b' => out.push('\u{8}'),
                        b'f' => out.push('\u{c}'),
                        b'u' => {
                            let hex =
                                std::str::from_utf8(&self.bytes[self.pos..self.pos + 4]).unwrap();
                            self.pos += 4;
                            let code = u32::from_str_radix(hex, 16).unwrap();
                            out.push(char::from_u32(code).unwrap_or('\u{fffd}'));
                        }
                        other => panic!("bad escape {:?}", other as char),
                    }
                }
                0 => panic!("unterminated string"),
                _ => {
                    // Copy a run of plain bytes, re-validating as UTF-8.
                    let start = self.pos - 1;
                    while !matches!(self.peek(), b'"' | b'\\' | 0) {
                        self.pos += 1;
                    }
                    out.push_str(std::str::from_utf8(&self.bytes[start..self.pos]).unwrap());
                }
            }
        }
    }

    fn array(&mut self) -> Json {
        self.expect(b'[');
        let mut items = Vec::new();
        self.ws();
        if self.peek() == b']' {
            self.pos += 1;
            return Json::Arr(items);
        }
        loop {
            items.push(self.value());
            self.ws();
            match self.peek() {
                b',' => self.pos += 1,
                b']' => {
                    self.pos += 1;
                    return Json::Arr(items);
                }
                other => panic!("unexpected {:?} in array", other as char),
            }
        }
    }

    fn object(&mut self) -> Json {
        self.expect(b'{');
        let mut fields = Vec::new();
        self.ws();
        if self.peek() == b'}' {
            self.pos += 1;
            return Json::Obj(fields);
        }
        loop {
            self.ws();
            let key = self.string();
            self.expect(b':');
            let value = self.value();
            fields.push((key, value));
            self.ws();
            match self.peek() {
                b',' => self.pos += 1,
                b'}' => {
                    self.pos += 1;
                    return Json::Obj(fields);
                }
                other => panic!("unexpected {:?} in object", other as char),
            }
        }
    }
}

#[test]
fn parses_nested_values() {
    let v = parse(r#"{"a": [1, -2.5, "x\"y", true, null], "b": {"c": 4294967280}}"#);
    assert_eq!(v.field("a").as_array().len(), 5);
    assert_eq!(v.field("a").as_array()[1].as_f64(), -2.5);
    assert_eq!(v.field("a").as_array()[2].as_str(), "x\"y");
    assert!(v.field("a").as_array()[3].as_bool());
    assert_eq!(v.field("a").as_array()[4], Json::Null);
    assert_eq!(v.field("b").field("c").as_u64(), 0xFFFF_FFF0);
}