Something went wrong. Try again.
Something went wrong. Try again.
7.3 kB · 234 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235use nom::{ IResult, Parser, branch::alt, bytes::complete::{tag, take_till1}, character::complete::{multispace0, one_of}, combinator::value, multi::separated_list0, number::complete::double, sequence::delimited,};use std::collections::HashMap;
#[derive(PartialEq, Debug, Clone)]pub enum JsonValue { Object(HashMap<String, JsonValue>), Array(Vec<JsonValue>), String(String), Float(f64), Integer(i64), Boolean(bool), Null,}
fn parse_null(input: &str) -> IResult<&str, JsonValue> { value(JsonValue::Null, tag("null")).parse(input)}fn parse_bool(input: &str) -> IResult<&str, JsonValue> { alt((value(true, tag("true")), value(false, tag("false")))) .map(JsonValue::Boolean) .parse(input)}fn parse_string(input: &str) -> IResult<&str, String> { delimited(tag("\""), parse_string_inner, tag("\"")).parse(input)}fn parse_string_inner(input: &str) -> IResult<&str, String> { let mut string_chunks = vec![]; let mut remaining = input; while let Ok((rem, parsed)) = alt(( parse_string_inner_naive.map(|s| s.to_string()), parse_escape_character.map(|s| s.to_string()), )) .parse(remaining) { remaining = rem; string_chunks.push(parsed) } Ok((remaining, string_chunks.concat()))}fn parse_string_inner_naive(input: &str) -> IResult<&str, &str> { take_till1(|c: char| c == '\"' || c == '\\' || c.is_control())(input)}fn parse_escape_character(input: &str) -> IResult<&str, char> { alt(( value('\"', tag("\\\"")), // value('\f', tag("\\f")), value('\\', tag("\\\\")), value('\n', tag("\\n")), value('/', tag("\\/")), value('\r', tag("\\r")), // value('\b', tag("\\b")), value('\t', tag("\\t")), //todo: unicode )) .parse(input)}
fn parse_array(input: &str) -> IResult<&str, JsonValue> { delimited( tag("["), separated_list0(tag(","), deserialize_json), tag("]"), ) .map(JsonValue::Array) .parse(input)}
fn parse_object(input: &str) -> IResult<&str, JsonValue> { delimited(tag("{"), separated_list0(tag(","), parse_pair), tag("}")) .map(|kvps| kvps.into_iter().collect::<HashMap<String, JsonValue>>()) .map(JsonValue::Object) .parse(input)}
fn parse_pair(input: &str) -> IResult<&str, (String, JsonValue)> { ( delimited(multispace0, parse_string, multispace0), tag(":"), deserialize_json, ) .map(|(k, _, v)| (k, v)) .parse(input)}fn parse_integer(input: &str) -> IResult<&str, JsonValue> { ( nom::character::complete::i64, nom::combinator::not(one_of("eE.")), ) .map(|(i, _)| JsonValue::Integer(i)) .parse(input)}
pub fn deserialize_json(json: &str) -> IResult<&str, JsonValue> { // map_res(); delimited( multispace0, alt(( parse_null, parse_bool, parse_integer, double.map(JsonValue::Float), parse_string.map(JsonValue::String), parse_array, parse_object, )), multispace0, ) .parse(json)}
#[cfg(test)]mod tests { use super::*; #[test] fn deserializes_null() { let null = deserialize_json("null").unwrap(); assert_eq!(null.1, JsonValue::Null) } #[test] fn deserializes_booleans() { let t = deserialize_json("true").unwrap(); let f = deserialize_json("false").unwrap(); assert_eq!(t.1, JsonValue::Boolean(true)); assert_eq!(f.1, JsonValue::Boolean(false)); } #[test] fn deserializes_strings() { let t = deserialize_json(r#""true""#).unwrap(); assert_eq!(t.1, JsonValue::String("true".to_string())); let f = deserialize_json(r#""false""#).unwrap(); assert_eq!(f.1, JsonValue::String("false".to_string())); } #[test] fn deserializes_escaped_strings() { let escaped = deserialize_json(r#"" \" \\ \n \/ \r \t ""#).unwrap(); assert_eq!( escaped.1, JsonValue::String(" \" \\ \n / \r \t ".to_string()) ); } #[test] fn invalid_escaped_strings() { deserialize_json(r#"" \g ""#).expect_err("should be an error"); }
#[test] fn deserializes_simple_arrays() { let t = deserialize_json("[]").unwrap(); assert_eq!(t.1, JsonValue::Array(vec![])); let f = deserialize_json("[true]").unwrap(); assert_eq!(f.1, JsonValue::Array(vec![JsonValue::Boolean(true)])); }
#[test] fn deserializes_arrays() { let f = deserialize_json("[true,false]").unwrap(); assert_eq!( f.1, JsonValue::Array(vec![JsonValue::Boolean(true), JsonValue::Boolean(false)]) ); } #[test] fn deserializes_nested_arrays() { let f = deserialize_json("[[true,false],[\"string!\"],[[[null],[],[]]]]").unwrap(); let empty_array = JsonValue::Array(vec![]); let boolean_array = JsonValue::Array(vec![JsonValue::Boolean(true), JsonValue::Boolean(false)]); let three_arrays = JsonValue::Array(vec![ JsonValue::Array(vec![JsonValue::Null]), empty_array.clone(), empty_array.clone(), ]); assert_eq!( f.1, JsonValue::Array(vec![ boolean_array, JsonValue::Array(vec![JsonValue::String("string!".to_string())]), JsonValue::Array(vec![three_arrays]) ]) ); } #[test] fn deserializes_simple_objects() { let t = deserialize_json("{}").unwrap(); assert_eq!(t.1, JsonValue::Object(HashMap::new())); let f = deserialize_json("{\"\":[]}").unwrap(); assert_eq!( f.1, JsonValue::Object( vec![(String::new(), JsonValue::Array(vec![]))] .into_iter() .collect() ) ); }
#[test] fn deserializes_object() { let result = deserialize_json(r#"{"includeLikes":true,"includeShares":true,"includeFollows":true,"includeReplies":true,"includeComments":true}"#).unwrap(); let JsonValue::Object(hm) = result.1 else { panic!() }; assert_eq!(hm.get("includeLikes"), Some(&JsonValue::Boolean(true))); assert_eq!(hm.get("includeShares"), Some(&JsonValue::Boolean(true))); assert_eq!(hm.get("includeFollows"), Some(&JsonValue::Boolean(true))); assert_eq!(hm.get("includeReplies"), Some(&JsonValue::Boolean(true))); }
#[test] fn deserializes_real_json() { let result = deserialize_json(include_str!("../data/user.json")).unwrap(); let JsonValue::Object(hm) = result.1 else { panic!() }; assert_eq!(hm.get("userId"), Some(&JsonValue::Integer(1234))) } #[test] fn deserializes_whitespaced_json() { let result = deserialize_json(include_str!("../data/whitespaced_user.json")).unwrap(); let JsonValue::Object(hm) = result.1 else { panic!() }; assert_eq!(hm.get("userId"), Some(&JsonValue::Integer(1234))) }}