use ktav::error::ErrorKind;
use ktav::thin::{parse_events, ParseEvent};
use ktav::{from_str, parse, Error, Value};
fn expect_key_a_1(src: &str) {
let value = parse(src).unwrap();
let Value::Object(obj) = value else {
panic!("{src:?}: expected Object root, got {value:?}");
};
assert_eq!(obj.len(), 1, "{src:?}: expected exactly one key");
assert_eq!(
obj.get("a"),
Some(&Value::Integer("1".into())),
"{src:?}: value mismatch"
); let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, serde_json::json!({"a": 1}), "{src:?}: serde mismatch");
let mut keys = Vec::new();
let mut ints = Vec::new();
parse_events(src, |e| match e {
ParseEvent::Key(k) => keys.push(k.to_string()),
ParseEvent::Integer(i) => ints.push(i.to_string()),
_ => {}
})
.unwrap();
assert_eq!(keys, vec!["a".to_string()], "{src:?}: thin keys mismatch");
assert_eq!(ints, vec!["1".to_string()], "{src:?}: thin ints mismatch");
}
#[test]
fn valid_trailing_comma_root_level() {
expect_key_a_1("{\"a\": 1, }\n");
expect_key_a_1("{\"a\": 1,\t}\n");
expect_key_a_1("{\"a\": 1, \u{00a0}}\n");
}
#[test]
fn valid_trailing_comma_nested() {
let src = "outer: {\"a\": 1, }\n";
let value = parse(src).unwrap();
let Value::Object(obj) = value else {
panic!("{src:?}: expected Object root");
};
let Value::Object(inner) = obj.get("outer").unwrap() else {
panic!("{src:?}: expected nested Object under `outer`");
};
assert_eq!(
inner.get("a"),
Some(&Value::Integer("1".into())),
"{src:?}: nested value mismatch"
);
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, serde_json::json!({"outer": {"a": 1}}));
let mut keys = Vec::new();
parse_events(src, |e| {
if let ParseEvent::Key(k) = e {
keys.push(k.to_string());
}
})
.unwrap();
assert_eq!(keys, vec!["outer".to_string(), "a".to_string()]);
let src = "outer: {\"a\": 1,\t}\n";
let value = parse(src).unwrap();
let Value::Object(obj) = value else {
panic!("{src:?}: expected Object root");
};
let Value::Object(inner) = obj.get("outer").unwrap() else {
panic!("{src:?}: expected nested Object under `outer`");
};
assert_eq!(inner.get("a"), Some(&Value::Integer("1".into())));
}
#[test]
fn quoted_key_and_value_before_trailing_comma() {
let src = "{ \"a b\": 1, }\n";
let value = parse(src).unwrap();
let Value::Object(obj) = value else {
panic!("{src:?}: expected Object root");
};
assert_eq!(obj.get("a b"), Some(&Value::Integer("1".into())));
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, serde_json::json!({"a b": 1}));
let mut keys = Vec::new();
parse_events(src, |e| {
if let ParseEvent::Key(k) = e {
keys.push(k.to_string());
}
})
.unwrap();
assert_eq!(keys, vec!["a b".to_string()]);
let src = "{ a: \"x\", }\n";
let value = parse(src).unwrap();
let Value::Object(obj) = value else {
panic!("{src:?}: expected Object root");
};
assert_eq!(obj.get("a"), Some(&Value::String("\"x\"".into())));
let v: serde_json::Value = from_str(src).unwrap();
assert_eq!(v, serde_json::json!({"a": "\"x\""}));
let mut strs = Vec::new();
parse_events(src, |e| {
if let ParseEvent::Str(s) = e {
strs.push(s.to_string());
}
})
.unwrap();
assert_eq!(strs, vec!["\"x\"".to_string()]);
}
#[test]
fn no_space_trailing_comma_still_fine() {
expect_key_a_1("{\"a\": 1,}\n");
match parse("{\"a\": 1,\n") {
Err(Error::Structured(ErrorKind::UnterminatedInlineCompound { .. })) => {}
other => panic!("expected UnterminatedInlineCompound, got {:?}", other.err()),
}
}
#[test]
fn dotted_key_trailing_ws_missing_separator() {
let src = "{ \"a\": 1, b. }\n";
match parse(src) {
Err(Error::Structured(ErrorKind::MalformedInlineCompound { .. })) => {}
other => panic!(
"{src:?}: expected MalformedInlineCompound, got {:?}",
other.err()
),
}
let err = from_str::<serde_json::Value>(src).unwrap_err();
assert!(
matches!(
err,
Error::Structured(ErrorKind::MalformedInlineCompound { .. })
),
"{src:?}: from_str expected MalformedInlineCompound, got {err:?}"
);
let thin = parse_events(src, |_| {}).unwrap_err();
assert!(
matches!(
thin,
Error::Structured(ErrorKind::MalformedInlineCompound { .. })
),
"{src:?}: thin expected MalformedInlineCompound, got {thin:?}"
);
let src = "outer: { \"a\": 1, b. }\n";
match parse(src) {
Err(Error::Structured(ErrorKind::MalformedInlineCompound { .. })) => {}
other => panic!(
"{src:?}: expected MalformedInlineCompound, got {:?}",
other.err()
),
}
let src = "{ \"a\": 1, b.: 2 }\n";
match parse(src) {
Err(Error::Structured(ErrorKind::EmptyKey { .. })) => {}
other => panic!("{src:?}: expected EmptyKey, got {:?}", other.err()),
}
}