#[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"),
_ => {
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);
}