mod feeder; mod prettyprinter; mod tokio; use std::fs; use actson::feeder::PushJsonFeeder; use actson::options::JsonParserOptionsBuilder; use actson::parser::ParserError; use actson::{JsonEvent, JsonParser}; use prettyprinter::PrettyPrinter; use serde_json::Value; /// Parse a JSON string and return a new JSON string generated by /// [`PrettyPrinter`]. Assert that the input JSON string is valid. fn parse(json: &str) -> String { let feeder = PushJsonFeeder::new(); parse_with_parser(json, &mut JsonParser::new(feeder)) } fn parse_with_parser(json: &str, parser: &mut JsonParser) -> String { let buf = json.as_bytes(); let mut prettyprinter = PrettyPrinter::new(); let mut i: usize = 0; while let Some(e) = parser.next_event().unwrap() { // feed as many bytes as possible to the parser if e == JsonEvent::NeedMoreInput { i += parser.feeder.push_bytes(&buf[i..]); if i == json.len() { parser.feeder.done(); } } prettyprinter.on_event(e, parser).unwrap(); } prettyprinter.get_result().to_string() } /// Parse a JSON string and expect parsing to fail fn parse_fail(json: &[u8]) -> ParserError { let feeder = PushJsonFeeder::new(); parse_fail_with_parser(json, &mut JsonParser::new(feeder)) } fn parse_fail_with_parser(json: &[u8], parser: &mut JsonParser) -> ParserError { let mut i: usize = 0; loop { // feed as many bytes as possible to the parser let e = match parser.next_event() { Err(err) => return err, Ok(Some(ne)) => ne, Ok(None) => panic!("End of file before error happened"), }; if e == JsonEvent::NeedMoreInput { i += parser.feeder.push_bytes(&json[i..]); if i == json.len() { parser.feeder.done(); } } } } /// Parse the given JSON string and check if the parser returns the correct number /// of consumed bytes for each event produced fn parse_checking_consumed_bytes(json: &str, events_bytes: &[(Option, usize)]) { let buf = json.as_bytes(); let mut parser = JsonParser::new(PushJsonFeeder::new()); for &(event, bytes) in events_bytes { let parsed_bytes = parser.parsed_bytes(); let next_event = parse_until_next_event(&buf[parsed_bytes..], &mut parser); let parsed_bytes = parser.parsed_bytes(); assert_eq!(next_event, event); assert_eq!(parsed_bytes, bytes); } } /// Parse the given JSON string and return the next event produced by the parser fn parse_until_next_event( json: &[u8], parser: &mut JsonParser, ) -> Option { let mut i: usize = 0; loop { let event = parser.next_event().unwrap(); match event { Some(JsonEvent::NeedMoreInput) => { i += parser.feeder.push_bytes(&json[i..]); if i == json.len() { parser.feeder.done(); } } Some(_) => return event, None => return None, } } } fn assert_json_eq(expected: &str, actual: &str) { let em: Value = serde_json::from_str(expected).unwrap(); let am: Value = serde_json::from_str(actual).unwrap(); assert_eq!(em, am); } /// Test if valid files can be parsed correctly #[test] fn test_pass() { for i in 1..=3 { let json = fs::read_to_string(format!("tests/fixtures/pass{}.txt", i)).unwrap(); assert_json_eq(&json, &parse(&json)); } } #[test] fn test_fail() { let feeder = PushJsonFeeder::new(); let mut parser = JsonParser::new_with_options( feeder, JsonParserOptionsBuilder::default() .with_max_depth(16) .build(), ); for i in 2..=34 { let json = fs::read_to_string(format!("tests/fixtures/fail{}.txt", i)).unwrap(); // ignore return value - we accept any error parse_fail_with_parser(json.as_bytes(), &mut parser); } } /// Test that an empty object is parsed correctly #[test] fn empty_object() { let json = r#"{}"#; assert_json_eq(json, &parse(json)); } /// Test that a simple object is parsed correctly #[test] fn simple_object() { let json = r#"{"name": "Elvis"}"#; assert_json_eq(json, &parse(json)); } /// Test that an empty array is parsed correctly #[test] fn empty_array() { let json = r#"[]"#; assert_json_eq(json, &parse(json)); } /// Test that a simple array is parsed correctly #[test] fn simple_array() { let json = r#"["Elvis", "Max"]"#; assert_json_eq(json, &parse(json)); } /// Test that an array with mixed values is parsed correctly #[test] fn mixed_array() { let json = r#"["Elvis", 132, "Max", 80.67]"#; assert_json_eq(json, &parse(json)); } /// Test that a JSON text containing a UTF-8 character is parsed correctly #[test] fn utf8() { let json = "{\"name\": \"Bj\u{0153}rn\"}"; assert_json_eq(json, &parse(json)); } #[test] fn too_many_next_event() { let json = "{}"; let feeder = PushJsonFeeder::new(); let mut parser = JsonParser::new(feeder); assert_json_eq(json, &parse_with_parser(json, &mut parser)); assert!(matches!(parser.next_event(), Err(ParserError::NoMoreInput))); } #[test] fn illegal_character() { let json = "{\"key\":\x02}"; assert!(matches!( parse_fail(json.as_bytes()), ParserError::IllegalInput(0x02) )); } #[test] fn syntax_error() { let json = "{key}"; assert!(matches!( parse_fail(json.as_bytes()), ParserError::SyntaxError )); } /// Make sure a number right before the end of the object can be parsed #[test] fn number_and_end_of_object() { let json = r#"{"n":2}"#; assert_json_eq(json, &parse(json)); } /// Make sure a fraction can be parsed #[test] fn fraction() { let json = r#"{"n":2.1}"#; assert_json_eq(json, &parse(json)); } /// Test that the parser does not accept illegal numbers ending with a dot #[test] fn illegal_number() { let json = r#"{"n":-2.}"#; assert!(matches!( parse_fail(json.as_bytes()), ParserError::SyntaxError )); } /// Make sure '0e1' can be parsed #[test] fn zero_with_exp() { let json = r#"{"n":0e1}"#; assert_json_eq(json, &parse(json)); } /// Test if a top-level empty string can be parsed #[test] fn top_level_empty_string() { let json = r#""""#; assert_json_eq(json, &parse(json)); } /// Test if a top-level 'false' can be parsed #[test] fn top_level_false() { let json = r#"false"#; assert_json_eq(json, &parse(json)); } /// Test if a top-level integer can be parsed #[test] fn top_level_int() { let json = r#"42"#; assert_json_eq(json, &parse(json)); } /// Test if a top-level long can be parsed #[test] fn top_level_long() { let json = r#"42123123123"#; assert_json_eq(json, &parse(json)); } /// Make sure pre-mature end of file is detected correctly #[test] fn number_and_eof() { let json = r#"{"i":42"#; assert!(matches!( parse_fail(json.as_bytes()), ParserError::NoMoreInput )); } /// Test if a top-level zero can be parsed #[test] fn top_level_zero() { let json = r#"0"#; assert_json_eq(json, &parse(json)); } /// Test if the parser returns an accurate number when calling the `parsed_bytes()` method #[test] fn number_of_processed_bytes() { // 16 // 1 7 |17 // ↓ ↓ ↓↓ // {"name": "Elvis"} let json = r#"{"name": "Elvis"}"#; // the events and the corresponding bytes that are processed to produce them let events_bytes = [ (Some(JsonEvent::StartObject), 1), (Some(JsonEvent::FieldName), 7), (Some(JsonEvent::ValueString), 16), (Some(JsonEvent::EndObject), 17), (None, 17), ]; parse_checking_consumed_bytes(json, &events_bytes); // 1 8 14 20 28 // ↓ ↓ ↓ ↓ ↓ // ["Elvis", 132, "Max", 80.67] let json = r#"["Elvis", 132, "Max", 80.67]"#; let events_bytes = [ (Some(JsonEvent::StartArray), 1), (Some(JsonEvent::ValueString), 8), (Some(JsonEvent::ValueInt), 14), (Some(JsonEvent::ValueString), 20), (Some(JsonEvent::ValueFloat), 28), (Some(JsonEvent::EndArray), 28), (None, 28), ]; parse_checking_consumed_bytes(json, &events_bytes); // œ is encoded as: 0xC5 0x93, so it is 2 bytes long // 17 // 1 7 |18 // ↓ ↓ ↓↓ // {"name": "Bjœrn"} let json = "{\"name\": \"Bj\u{0153}rn\"}"; let events_bytes = [ (Some(JsonEvent::StartObject), 1), (Some(JsonEvent::FieldName), 7), (Some(JsonEvent::ValueString), 17), (Some(JsonEvent::EndObject), 18), (None, 18), ]; parse_checking_consumed_bytes(json, &events_bytes); } /// Test if the parser is able to process all valid files from the test suite #[test] fn test_suite_pass() { let files = fs::read_dir("tests/json_test_suite/test_parsing").unwrap(); for f in files { let f = f.unwrap(); let name = f.file_name(); if name.to_str().unwrap().starts_with('y') { let json = fs::read_to_string(f.path()).unwrap(); if name == "y_number_minus_zero.json" || name == "y_number_negative_zero.json" { // -0 equals 0 assert_eq!("[\n 0\n]", &parse(&json)); } else { assert_json_eq(&json, &parse(&json)); } } } } /// Test if the parser actually fails to process each invalid file from the test suite #[test] fn test_suite_fail() { let files = fs::read_dir("tests/json_test_suite/test_parsing").unwrap(); for f in files { let f = f.unwrap(); let name = f.file_name(); if name.to_str().unwrap().starts_with('n') { let json = fs::read(f.path()).unwrap(); parse_fail(&json); // ignore return value - we accept any error } } } /// Test if multiple top-level numbers can be parsed in streaming mode #[test] fn streaming_numbers() { let options = JsonParserOptionsBuilder::default() .with_streaming(true) .build(); let json = r#"1 2 3 4 5"#; let feeder = PushJsonFeeder::new(); let r = parse_with_parser(json, &mut JsonParser::new_with_options(feeder, options)); assert_eq!("12345", r); } /// Test if multiple top-level strings can be parsed in streaming mode #[test] fn streaming_strings() { let options = JsonParserOptionsBuilder::default() .with_streaming(true) .build(); let json = r#""1""2" "3""4" "5""#; let feeder = PushJsonFeeder::new(); let r = parse_with_parser(json, &mut JsonParser::new_with_options(feeder, options)); assert_eq!(r#""1""2""3""4""5""#, r); } /// Test if multiple top-level arrays can be parsed in streaming mode #[test] fn streaming_arrays() { let options = JsonParserOptionsBuilder::default() .with_streaming(true) .build(); let json = r#"[1][2][3][4][5]"#; let feeder = PushJsonFeeder::new(); let r = parse_with_parser(json, &mut JsonParser::new_with_options(feeder, options)); assert_eq!("[\n 1\n][\n 2\n][\n 3\n][\n 4\n][\n 5\n]", r); } /// Test if multiple top-level objects can be parsed in streaming mode #[test] fn streaming_objects() { let options = JsonParserOptionsBuilder::default() .with_streaming(true) .build(); let json = r#"{"a":1}{"b":2}{"c":3}{"d":4}{"e":5}"#; let feeder = PushJsonFeeder::new(); let r = parse_with_parser(json, &mut JsonParser::new_with_options(feeder, options)); assert_eq!( r#"{ "a": 1 }{ "b": 2 }{ "c": 3 }{ "d": 4 }{ "e": 5 }"#, r ); } /// Test if multiple top-level numbers can be parsed in streaming mode #[test] fn streaming_complex() { let options = JsonParserOptionsBuilder::default() .with_streaming(true) .build(); let json = r#"1.0 2"3"{"a":4}5 6e-5["b"]["c"] 7{"d":"e"}true8falsenull"#; let feeder = PushJsonFeeder::new(); let r = parse_with_parser(json, &mut JsonParser::new_with_options(feeder, options)); assert_eq!( r#"1.02"3"{ "a": 4 }50.00006[ "b" ][ "c" ]7{ "d": "e" }true8falsenull"#, r ); }