Lines
100 %
Functions
98.7 %
Branches
#[cfg(feature = "alloc")]
use alloc::{
string::{String, ToString},
vec,
};
use core::fmt::Write;
use std::println;
use crate::anystr::AnyStr;
use crate::doc::Document;
use crate::parser::{ParseConfig, ParseDelegate, Parser};
use crate::string::StringContents;
use crate::value::ValueParser;
use crate::{Error, ErrorKind, JsonNumber, JsonString, JsonStringInfo, Object, Value};
#[track_caller]
fn test_json_parse(source: &[u8], value: &Value<'_>) {
println!("Testing slice {}", std::str::from_utf8(source).unwrap());
let parsed_value = Value::from_json_bytes(source).unwrap();
assert_eq!(&parsed_value, value);
let doc = Document::from_json_bytes(source).unwrap();
assert_eq!(Value::from(doc), parsed_value);
}
#[test]
fn keywords() {
test_json_parse(b"true", &Value::from(true));
test_json_parse(b"false", &Value::from(false));
test_json_parse(b"null", &Value::Null);
fn empty_array() {
test_json_parse(b"[]", &Value::Array(vec![]));
fn one_element_array() {
test_json_parse(b"[true]", &Value::Array(vec![Value::from(true)]));
fn two_element_array() {
test_json_parse(
b"[true,false]",
&Value::Array(vec![Value::from(true), Value::from(false)]),
);
fn spaced_out_array() {
b" [ true , false ] ",
fn whitespace() {
test_json_parse(b" \t\n\rnull", &Value::Null);
fn basic_string() {
br#""hello""#,
&Value::from(JsonString {
source: StringContents::Json(AnyStr::Borrowed("hello")),
info: JsonStringInfo::new(false, 5),
}),
br#""""#,
source: StringContents::Json(AnyStr::Borrowed("")),
info: JsonStringInfo::new(false, 0),
fn escapey_string() {
br#""\"\\\/\b\f\n\r\t\u25eF""#,
source: StringContents::Json(AnyStr::Borrowed(r#"\"\\\/\b\f\n\r\t\u25eF"#)),
info: JsonStringInfo::new(true, 11),
fn surrogate_pair() {
br#""\ud83d\ude39\ud83d\udc8d""#,
&Value::from(JsonString::from_json("\"\u{1f639}\u{1f48d}\"").unwrap()),
assert_eq!(
JsonString::from_json(r#""\ud83d\ude39\ud83d\udc8d""#,).unwrap(),
"\u{1f639}\u{1f48d}"
fn empty_object() {
test_json_parse(b"{}", &Value::Object(Object::new()));
fn one_mapping() {
br#"{"hello":true}"#,
&Value::Object(Object::from_iter([(
JsonString {
},
Value::from(true),
)])),
fn two_mappings() {
br#"{"hello":true,"world":null}"#,
&Value::Object(Object::from_iter([
(
),
source: StringContents::Json(AnyStr::Borrowed("world")),
Value::Null,
])),
fn spaced_out_object() {
br#" { "hello" : true , "world" : null } "#,
JsonString::from_json("\"hello\"").unwrap(),
(JsonString::from_json("\"world\"").unwrap(), Value::Null),
fn numbers() {
for b in b'0'..=b'9' {
&[b],
&Value::Number(JsonNumber {
source: AnyStr::Borrowed(std::str::from_utf8(&[b]).unwrap()),
b"-1",
source: AnyStr::Borrowed("-1"),
b"-1.0",
source: AnyStr::Borrowed("-1.0"),
b"-1.0e1",
source: AnyStr::Borrowed("-1.0e1"),
b"-1.0E-1",
source: AnyStr::Borrowed("-1.0E-1"),
fn object_of_everything() {
br#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#,
JsonString::from_json(r#""a""#).unwrap(),
Value::Number(JsonNumber {
source: AnyStr::Borrowed("1"),
(JsonString::from_json(r#""b""#).unwrap(), Value::from(true)),
JsonString::from_json(r#""c""#).unwrap(),
Value::from(JsonString::from_json(r#""hello""#).unwrap()),
JsonString::from_json(r#""d""#).unwrap(),
Value::Array(vec![]),
JsonString::from_json(r#""e""#).unwrap(),
Value::Object(Object::new()),
fn array_of_everything() {
br#"[1,true,"hello",[],{}]"#,
&Value::Array(vec![
]),
fn expect_json_error(json: &str) -> Error {
println!("Parsing {json}");
let err = Value::from_json(json).expect_err("parsing did not error");
println!("> {err:?}");
err
macro_rules! assert_json_error_kind_matches {
($json:expr, $offset:expr, $kind:expr) => {{
let err = expect_json_error($json);
assert_eq!(err.kind(), &$kind);
assert_eq!(err.offset(), $offset);
}};
fn object_errors() {
assert_json_error_kind_matches!("{1:true}", 1, ErrorKind::ObjectKeysMustBeStrings);
assert_json_error_kind_matches!(r#"{"a": true,}"#, 11, ErrorKind::IllegalTrailingComma);
assert_json_error_kind_matches!(r#"{"a": true,:"#, 11, ErrorKind::ExpectedObjectKey);
assert_json_error_kind_matches!(r#"{"a"}"#, 4, ErrorKind::ExpectedColon);
assert_json_error_kind_matches!(r#"{"a""#, 4, ErrorKind::ExpectedColon);
assert_json_error_kind_matches!(r#"{"a":}"#, 5, ErrorKind::Unexpected(b'}'));
assert_json_error_kind_matches!(r#"{"a":1]"#, 6, ErrorKind::ExpectedCommaOrEndOfObject);
assert_json_error_kind_matches!(r#"{"a": true,"#, 11, ErrorKind::UnclosedObject);
fn array_errors() {
assert_json_error_kind_matches!("[1,]", 3, ErrorKind::IllegalTrailingComma);
assert_json_error_kind_matches!("[1}", 2, ErrorKind::ExpectedCommaOrEndOfArray);
assert_json_error_kind_matches!("[1,,}", 3, ErrorKind::Unexpected(b','));
assert_json_error_kind_matches!("[1,", 3, ErrorKind::UnclosedArray);
assert_json_error_kind_matches!("[", 1, ErrorKind::UnclosedArray);
fn keyword_errors() {
assert_json_error_kind_matches!("tru ", 3, ErrorKind::Unexpected(b' '));
assert_json_error_kind_matches!("tru", 3, ErrorKind::UnexpectedEof);
fn json_errors() {
assert_json_error_kind_matches!("true true", 5, ErrorKind::TrailingNonWhitespace);
assert_json_error_kind_matches!(",", 0, ErrorKind::Unexpected(b','));
assert_json_error_kind_matches!("#", 0, ErrorKind::Unexpected(b'#'));
assert_json_error_kind_matches!("", 0, ErrorKind::UnexpectedEof);
fn string_errors() {
assert_json_error_kind_matches!(r#"""#, 1, ErrorKind::UnclosedString);
assert_json_error_kind_matches!("\"\0\"", 1, ErrorKind::Unexpected(b'\0'));
assert_json_error_kind_matches!(r#""\?"#, 2, ErrorKind::InvalidEscape);
assert_json_error_kind_matches!(r#""\udddd "#, 2, ErrorKind::Utf8);
assert_json_error_kind_matches!(r#""\udda1 "#, 2, ErrorKind::Utf8);
assert_json_error_kind_matches!(r#""\uG"#, 3, ErrorKind::InvalidHexadecimal);
println!("Parsing invalid unicode");
let err = Value::from_json_bytes(b"\"\xdd\xdd\"").expect_err("parsing did not error");
assert!(matches!(err.kind(), ErrorKind::Utf8));
fn number_errors() {
assert_json_error_kind_matches!("- ", 1, ErrorKind::ExpectedDigit);
assert_json_error_kind_matches!("1. ", 2, ErrorKind::ExpectedDecimalDigit);
assert_json_error_kind_matches!("1.0E ", 4, ErrorKind::ExpectedExponent);
assert_json_error_kind_matches!("1.0E- ", 5, ErrorKind::ExpectedExponent);
// Same battery of tests but with an eof instead
assert_json_error_kind_matches!("-", 1, ErrorKind::ExpectedDigit);
assert_json_error_kind_matches!("1.", 2, ErrorKind::ExpectedDecimalDigit);
assert_json_error_kind_matches!("1.0E", 4, ErrorKind::ExpectedExponent);
assert_json_error_kind_matches!("1.0E-", 5, ErrorKind::ExpectedExponent);
fn test_roundtrip_encoding(source: &str) {
println!("Testing {source}");
let value = Value::from_json(source).unwrap();
assert_eq!(value.to_json(), source);
fn test_roundtrip_encoding_pretty_custom(source: &str, indentation: &str, line_ending: &str) {
value.to_json_pretty_custom(indentation, line_ending),
source
fn test_roundtrip_encoding_pretty(source: &str) {
assert_eq!(value.to_json_pretty(), source);
fn json_formatting() {
test_roundtrip_encoding(r#"[1,true,"hello",[],{}]"#);
test_roundtrip_encoding(r#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#);
test_roundtrip_encoding_pretty("[\n 1,\n true,\n \"hello\",\n [],\n {}\n]");
test_roundtrip_encoding_pretty(
"{\n \"a\": 1,\n \"b\": true,\n \"c\": \"hello\",\n \"d\": [],\n \"e\": {}\n}",
test_roundtrip_encoding_pretty_custom(
"{\r\t\"a\": 1,\r\t\"b\": true,\r\t\"c\": \"hello\",\r\t\"d\": [],\r\t\"e\": {}\r}",
"\t",
"\r",
test_roundtrip_encoding(r#""\u0000""#);
fn value_display() {
let value = Value::from_json(r#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#).unwrap();
value.to_string(),
r#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#
let mut pretty = String::new();
write!(&mut pretty, "{value:#}").unwrap();
pretty,
"{\n \"a\": 1,\n \"b\": true,\n \"c\": \"hello\",\n \"d\": [],\n \"e\": {}\n}"
struct ErroringDelegate {
parser: ValueParser,
error_on: ErrorOn,
impl ErroringDelegate {
pub fn new(error_on: ErrorOn) -> Self {
Self {
error_on,
#[derive(Debug, Eq, PartialEq)]
enum ErrorOn {
None,
Null,
Boolean(bool),
Number(&'static str),
String,
BeginObject,
ObjectKey,
ObjectValue,
EndObject,
BeginArray,
ArrayValue,
EndArray,
#[derive(Eq, PartialEq, Debug)]
struct MyError;
impl<'a> ParseDelegate<'a> for ErroringDelegate {
type Array = <ValueParser as ParseDelegate<'a>>::Array;
type Error = MyError;
type Key = JsonString<'a>;
type Object = Object<'a>;
type Value = Value<'a>;
fn null(&mut self) -> Result<Self::Value, Self::Error> {
if matches!(self.error_on, ErrorOn::Null) {
Err(MyError)
} else {
Ok(self.parser.null().unwrap())
fn boolean(&mut self, value: bool) -> Result<Self::Value, Self::Error> {
if self.error_on == ErrorOn::Boolean(value) {
Ok(self.parser.boolean(value).unwrap())
fn number(&mut self, value: JsonNumber<'a>) -> Result<Self::Value, Self::Error> {
match &self.error_on {
ErrorOn::Number(number) if value.source() == *number => Err(MyError),
_ => Ok(self.parser.number(value).unwrap()),
fn string(&mut self, value: JsonString<'a>) -> Result<Self::Value, Self::Error> {
if matches!(self.error_on, ErrorOn::String) {
Ok(self.parser.string(value).unwrap())
fn begin_object(&mut self) -> Result<Self::Object, Self::Error> {
if matches!(self.error_on, ErrorOn::BeginObject) {
Ok(self.parser.begin_object().unwrap())
fn object_key(
&mut self,
object: &mut Self::Object,
key: JsonString<'a>,
) -> Result<Self::Key, Self::Error> {
if matches!(self.error_on, ErrorOn::ObjectKey) {
Ok(self.parser.object_key(object, key).unwrap())
fn object_value(
key: Self::Key,
value: Self::Value,
) -> Result<(), Self::Error> {
if matches!(self.error_on, ErrorOn::ObjectValue) {
self.parser.object_value(object, key, value).unwrap();
Ok(())
fn object_is_empty(&self, object: &Self::Object) -> bool {
self.parser.object_is_empty(object)
fn end_object(&mut self, object: Self::Object) -> Result<Self::Value, Self::Error> {
if matches!(self.error_on, ErrorOn::EndObject) {
Ok(self.parser.end_object(object).unwrap())
fn begin_array(&mut self) -> Result<Self::Array, Self::Error> {
if matches!(self.error_on, ErrorOn::BeginArray) {
Ok(self.parser.begin_array().unwrap())
fn array_value(
array: &mut Self::Array,
if matches!(self.error_on, ErrorOn::ArrayValue) {
self.parser.array_value(array, value).unwrap();
fn array_is_empty(&self, array: &Self::Array) -> bool {
self.parser.array_is_empty(array)
fn end_array(&mut self, array: Self::Array) -> Result<Self::Value, Self::Error> {
if matches!(self.error_on, ErrorOn::EndArray) {
Ok(self.parser.end_array(array).unwrap())
fn kind_of(&self, value: &Self::Value) -> crate::parser::JsonKind {
self.parser.kind_of(value)
fn parse_delegate_error() {
let payload =
br#"{"a":1,"b":true,"c":"hello","d":[null, -1, 0, false, []],"e":{},"f":"error"}"#;
Parser::parse_json_bytes_with_config(
payload,
ParseConfig::strict(),
ErroringDelegate::new(ErrorOn::None),
)
.expect("no errors");
for error_on in [
ErrorOn::Null,
ErrorOn::Boolean(false),
ErrorOn::Number("1"),
ErrorOn::Number("-1"),
ErrorOn::Number("0"),
ErrorOn::String,
ErrorOn::BeginObject,
ErrorOn::ObjectKey,
ErrorOn::ObjectValue,
ErrorOn::EndObject,
ErrorOn::BeginArray,
ErrorOn::ArrayValue,
ErrorOn::EndArray,
] {
println!("Trying to error on {error_on:?}");
let err = Parser::parse_json_bytes(payload, ErroringDelegate::new(error_on))
.expect_err("expecting delegate error");
assert_eq!(err.kind(), &ErrorKind::ErrorFromDelegate(MyError));
fn illegal_types_at_root() {
fn test_payload(json: &str) {
Document::from_json_with_config(json, ParseConfig::strict())
.unwrap_err()
.kind,
ErrorKind::PayloadsShouldBeObjectOrArray
Value::from_json_with_config(json, ParseConfig::strict())
test_payload("null");
test_payload("true");
test_payload("1");
test_payload("\"\"");