use ktav::error::ErrorKind;
use ktav::thin::{parse_events, ParseEvent};
use ktav::{from_str, parse, Error, ObjectMap, Value};
#[derive(Debug, PartialEq)]
enum Ev {
Null,
Bool(bool),
Integer(String),
Float(String),
Str(String),
Key(String),
BeginObject,
EndObject,
BeginArray,
EndArray,
}
fn collect(src: &str) -> Vec<Ev> {
let mut out = Vec::new();
parse_events(src, |e| {
out.push(match e {
ParseEvent::Null => Ev::Null,
ParseEvent::Bool(b) => Ev::Bool(b),
ParseEvent::Integer(s) => Ev::Integer(s.to_string()),
ParseEvent::Float(s) => Ev::Float(s.to_string()),
ParseEvent::Str(s) => Ev::Str(s.to_string()),
ParseEvent::Key(s) => Ev::Key(s.to_string()),
ParseEvent::BeginObject => Ev::BeginObject,
ParseEvent::EndObject => Ev::EndObject,
ParseEvent::BeginArray => Ev::BeginArray,
ParseEvent::EndArray => Ev::EndArray,
_ => unreachable!(),
});
})
.unwrap();
out
}
fn obj(pairs: &[(&str, Value)]) -> Value {
let mut m = ObjectMap::default();
for (k, v) in pairs {
m.insert((*k).into(), v.clone());
}
Value::Object(m)
}
fn arr(items: &[Value]) -> Value {
Value::Array(items.to_vec())
}
#[test]
fn nested_raw_object_in_array() {
let src = r"[{a:: x}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("a", Value::String("x".into()))])]),
"raw-scalar nested repro failed for {src:?}"
);
let src = r#"[{"a":: x}]
"#;
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("a", Value::String("x".into()))])]),
"quoted-key raw-scalar nested repro failed for {src:?}"
);
let src = r"[{a:: x}, 2]
";
assert_eq!(
parse(src).unwrap(),
arr(&[
obj(&[("a", Value::String("x".into()))]),
Value::Integer("2".into()),
]),
"sibling item after nested raw compound failed for {src:?}"
);
}
#[test]
fn nested_double_colon_scalar_array_in_object() {
let src = r"{a: [:: x]}
";
assert_eq!(
parse(src).unwrap(),
obj(&[("a", arr(&[Value::String(":: x".into())]))]),
"double-colon scalar in nested array failed for {src:?}"
);
let src = r#"{"a": [:: x]}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[("a", arr(&[Value::String(":: x".into())]))]),
"quoted-key double-colon scalar in nested array failed for {src:?}"
);
let src = r"{a: [:: x], b: 2}
";
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::String(":: x".into())])),
("b", Value::Integer("2".into())),
]),
"sibling pair after nested compound failed for {src:?}"
);
}
#[test]
fn raw_content_openers_stay_literal() {
let src = r"[{a:: x[}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("a", Value::String("x[".into()))])]),
"literal opener in nested raw value failed for {src:?}"
);
let src = r"[{a:: x\[}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("a", Value::String("x[".into()))])]),
"escaped opener in nested raw value failed for {src:?}"
);
}
#[test]
fn raw_terminated_by_any_unescaped_closer() {
let src = r"{a:: x]}
";
match parse(src) {
Err(Error::Structured(ErrorKind::UnterminatedInlineCompound { .. })) => {}
other => panic!("case 34: expected UnterminatedInlineCompound, got {other:?}"),
}
let src = r#"{"a":: x]}
"#;
match parse(src) {
Err(Error::Structured(ErrorKind::UnterminatedInlineCompound { .. })) => {}
other => panic!("case 35: expected UnterminatedInlineCompound, got {other:?}"),
}
let src = r"{a:: x{y]}
";
match parse(src) {
Err(Error::Structured(ErrorKind::UnterminatedInlineCompound { .. })) => {}
other => panic!("case 7: expected UnterminatedInlineCompound, got {other:?}"),
}
let src = r"[{a:: x]}]
";
match parse(src) {
Err(Error::Structured(ErrorKind::UnterminatedInlineCompound { .. })) => {}
other => panic!("case 9: expected UnterminatedInlineCompound, got {other:?}"),
}
}
#[test]
fn raw_escaped_closer_stays_content() {
let src = r"{a:: x\]}
";
assert_eq!(
parse(src).unwrap(),
obj(&[("a", Value::String("x]".into()))]),
"escaped closer control failed for {src:?}"
);
let src = r#"{"a":: x\]}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[("a", Value::String("x]".into()))]),
"slow-path escaped closer control failed for {src:?}"
);
let src = r"{a:: x{y\]}
";
assert_eq!(
parse(src).unwrap(),
obj(&[("a", Value::String("x{y]".into()))]),
"escaped closer after literal opener failed for {src:?}"
);
let src = r"[{a:: x\]}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("a", Value::String("x]".into()))])]),
"escaped crossed closer failed for {src:?}"
);
}
#[test]
fn raw_terminated_by_comma_mid_nesting() {
let src = r"[{a:: x, b: 2}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[
("a", Value::String("x".into())),
("b", Value::Integer("2".into())),
])]),
"comma-terminated raw scalar mid-nesting failed for {src:?}"
);
}
#[test]
fn raw_same_kind_nesting_and_deeper() {
let src = r"[[:: x]]
";
assert_eq!(
parse(src).unwrap(),
arr(&[arr(&[Value::String(":: x".into())])]),
"same-kind double-colon nesting failed for {src:?}"
);
let src = r"{k: [{a:: x}]}
";
assert_eq!(
parse(src).unwrap(),
obj(&[("k", arr(&[obj(&[("a", Value::String("x".into()))])]))]),
"deeper raw nesting failed for {src:?}"
);
}
#[test]
fn scope_restore_after_array_close() {
let src = r#"{a: [1], "x}": 2}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("x}", Value::Integer("2".into())),
]),
"scope restore after array close failed for {src:?}"
);
let src = r"{a: [1], 'x}': 2}
";
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("x}", Value::Integer("2".into())),
]),
"scope restore, single-quote key failed for {src:?}"
);
let src = "{a: [1], `x}`: 2}\n";
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("x}", Value::Integer("2".into())),
]),
"scope restore, backtick key failed for {src:?}"
);
let src = r#"{a: [1], "x[": 2}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("x[", Value::Integer("2".into())),
]),
"scope restore, bracket-containing key failed for {src:?}"
);
let src = r#"{a: [1], "x]": 2}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("x]", Value::Integer("2".into())),
]),
"scope restore, close-bracket key failed for {src:?}"
);
}
#[test]
fn scope_restore_mixed_nesting() {
let src = r#"{a: [1], b: {c: 2}, "x]": 3}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", arr(&[Value::Integer("1".into())])),
("b", obj(&[("c", Value::Integer("2".into()))])),
("x]", Value::Integer("3".into())),
]),
"mixed Array-then-Object restore failed for {src:?}"
);
let src = r#"{a: {b: 1}, c: [2], "x}": 3}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", obj(&[("b", Value::Integer("1".into()))])),
("c", arr(&[Value::Integer("2".into())])),
("x}", Value::Integer("3".into())),
]),
"mixed Object-then-Array restore failed for {src:?}"
);
let src = r#"[{a: 1}, {"b}": 2}]
"#;
assert_eq!(
parse(src).unwrap(),
arr(&[
obj(&[("a", Value::Integer("1".into()))]),
obj(&[("b}", Value::Integer("2".into()))]),
]),
"quoted key in second array element failed for {src:?}"
);
let src = r#"{a: [1, {b: 2}], "x}": 3}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
(
"a",
arr(&[
Value::Integer("1".into()),
obj(&[("b", Value::Integer("2".into()))]),
])
),
("x}", Value::Integer("3".into())),
]),
"deepest stack restore failed for {src:?}"
);
}
#[test]
fn controls_unchanged() {
let src = r#"{a: {b: 1}, "x}": 2}
"#;
assert_eq!(
parse(src).unwrap(),
obj(&[
("a", obj(&[("b", Value::Integer("1".into()))])),
("x}", Value::Integer("2".into())),
]),
"object-nesting control failed for {src:?}"
);
let src = r"[{b: 1}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[obj(&[("b", Value::Integer("1".into()))])]),
"control 22 failed for {src:?}"
);
let src = r"[1, [2], {a: 3}]
";
assert_eq!(
parse(src).unwrap(),
arr(&[
Value::Integer("1".into()),
arr(&[Value::Integer("2".into())]),
obj(&[("a", Value::Integer("3".into()))]),
]),
"control 23 failed for {src:?}"
);
}
#[test]
fn error_pins_unchanged() {
let err = parse("[{a:: x}\n").unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 24: expected UnterminatedInlineCompound, got {other:?}"),
}
let err = parse("{a: [:: x\n").unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 25: expected UnterminatedInlineCompound, got {other:?}"),
}
let err = parse("{a: [1}\n").unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 26: expected UnterminatedInlineCompound, got {other:?}"),
}
let err = parse("[{a: 1]}\n").unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 27: expected UnterminatedInlineCompound, got {other:?}"),
}
}
#[test]
fn thin_api() {
let src = r"[{a:: x}]
";
assert_eq!(
collect(src),
vec![
Ev::BeginArray,
Ev::BeginObject,
Ev::Key("a".into()),
Ev::Str("x".into()),
Ev::EndObject,
Ev::EndArray,
],
"thin events for raw nested repro failed for {src:?}"
);
let src = r#"{a: [1], "x}": 2}
"#;
assert_eq!(
collect(src),
vec![
Ev::BeginObject,
Ev::Key("a".into()),
Ev::BeginArray,
Ev::Integer("1".into()),
Ev::EndArray,
Ev::Key("x}".into()),
Ev::Integer("2".into()),
Ev::EndObject,
],
"thin events for scope-restore repro failed for {src:?}"
);
let src = r#"{a: [1, {b: 2}], "x}": 3}
"#;
assert_eq!(
collect(src),
vec![
Ev::BeginObject,
Ev::Key("a".into()),
Ev::BeginArray,
Ev::Integer("1".into()),
Ev::BeginObject,
Ev::Key("b".into()),
Ev::Integer("2".into()),
Ev::EndObject,
Ev::EndArray,
Ev::Key("x}".into()),
Ev::Integer("3".into()),
Ev::EndObject,
],
"thin events for deepest restore failed for {src:?}"
);
}
#[test]
fn thin_api_raw_closer_termination() {
let src = r"{a:: x\]}
";
assert_eq!(
collect(src),
vec![
Ev::BeginObject,
Ev::Key("a".into()),
Ev::Str("x]".into()),
Ev::EndObject,
],
"thin events for escaped-closer control failed for {src:?}"
);
let src = r"{a:: x]}
";
let err = parse_events(src, |_| {}).unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 42: expected UnterminatedInlineCompound, got {other:?}"),
}
let src = r"[{a:: x, b: 2}]
";
assert_eq!(
collect(src),
vec![
Ev::BeginArray,
Ev::BeginObject,
Ev::Key("a".into()),
Ev::Str("x".into()),
Ev::Key("b".into()),
Ev::Integer("2".into()),
Ev::EndObject,
Ev::EndArray,
],
"thin events for comma-terminated raw failed for {src:?}"
);
}
#[test]
fn typed_api() {
use serde_json::json;
let src = r"[{a:: x}]
";
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, json!([{"a": "x"}]), "typed raw nested repro failed");
let src = r#"{a: [1], "x}": 2}
"#;
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(
v,
json!({"a": [1], "x}": 2}),
"typed scope-restore repro failed for {src:?}"
);
}
#[test]
fn typed_api_raw_closer_termination() {
use serde_json::json;
let src = r"{a:: x\]}
";
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, json!({"a": "x]"}), "typed escaped-closer control failed");
let src = r"{a:: x]}
";
let err = from_str::<serde_json::Value>(src).unwrap_err();
match &err {
Error::Structured(ErrorKind::UnterminatedInlineCompound { .. }) => {}
other => panic!("case 45: expected UnterminatedInlineCompound, got {other:?}"),
}
}