use ktav::thin::{parse_events, ParseEvent};
use ktav::{Error, ErrorKind};
#[derive(Debug, PartialEq, Eq)]
enum Owned {
Null,
Bool(bool),
Integer(String),
Float(String),
Str(String),
Key(String),
BeginObject,
EndObject,
BeginArray,
EndArray,
}
fn collect(input: &str) -> Vec<Owned> {
let mut out = Vec::new();
parse_events(input, |ev| {
out.push(match ev {
ParseEvent::Null => Owned::Null,
ParseEvent::Bool(b) => Owned::Bool(b),
ParseEvent::Integer(s) => Owned::Integer(s.to_string()),
ParseEvent::Float(s) => Owned::Float(s.to_string()),
ParseEvent::Str(s) => Owned::Str(s.to_string()),
ParseEvent::Key(k) => Owned::Key(k.to_string()),
ParseEvent::BeginObject => Owned::BeginObject,
ParseEvent::EndObject => Owned::EndObject,
ParseEvent::BeginArray => Owned::BeginArray,
ParseEvent::EndArray => Owned::EndArray,
_ => unreachable!("ParseEvent is non_exhaustive but all known variants handled"),
});
})
.unwrap();
out
}
#[test]
fn flat_pairs_emit_expected_sequence() {
let src = "port: 8080\nhost: example.com\n";
let events = collect(src);
assert_eq!(
events,
vec![
Owned::BeginObject,
Owned::Key("port".into()),
Owned::Str("8080".into()),
Owned::Key("host".into()),
Owned::Str("example.com".into()),
Owned::EndObject,
]
);
}
#[test]
fn nested_object_brackets_inner_pair() {
let src = "cfg: {\n port: 8080\n}\n";
let events = collect(src);
assert_eq!(
events,
vec![
Owned::BeginObject,
Owned::Key("cfg".into()),
Owned::BeginObject,
Owned::Key("port".into()),
Owned::Str("8080".into()),
Owned::EndObject,
Owned::EndObject,
]
);
}
#[test]
fn array_with_marker_items() {
let src = "items: [\n :: literal\n :i 42\n :f 3.14\n]\n";
let events = collect(src);
assert_eq!(
events,
vec![
Owned::BeginObject,
Owned::Key("items".into()),
Owned::BeginArray,
Owned::Str("literal".into()),
Owned::Integer("42".into()),
Owned::Float("3.14".into()),
Owned::EndArray,
Owned::EndObject,
]
);
}
#[test]
fn invalid_input_returns_structured_error_matching_parse() {
let src = "anchor: ok\nport:8080\n";
let parse_err = ktav::parse(src).expect_err("must error");
let callback_err = parse_events(src, |_| {
})
.expect_err("must error");
match (&parse_err, &callback_err) {
(
Error::Structured(ErrorKind::MissingSeparatorSpace { line: l1, .. }),
Error::Structured(ErrorKind::MissingSeparatorSpace { line: l2, .. }),
) => {
assert_eq!(l1, l2);
}
other => panic!("error kinds did not match: {other:?}"),
}
}
#[test]
fn dotted_key_resolved_into_synthetic_compound_events() {
let src = "a.b: v\n";
let events = collect(src);
assert_eq!(
events,
vec![
Owned::BeginObject,
Owned::Key("a".into()),
Owned::BeginObject,
Owned::Key("b".into()),
Owned::Str("v".into()),
Owned::EndObject,
Owned::EndObject,
]
);
}
#[test]
fn event_str_slices_borrow_from_input() {
let src = "port: 8080\nhost: example.com\n";
let input_start = src.as_ptr() as usize;
let input_end = input_start + src.len();
let mut borrowed_count = 0_usize;
parse_events(src, |ev| match ev {
ParseEvent::Str(s) | ParseEvent::Key(s) => {
let p = s.as_ptr() as usize;
if p >= input_start && p + s.len() <= input_end {
borrowed_count += 1;
}
}
_ => {}
})
.unwrap();
assert_eq!(borrowed_count, 4);
}
#[test]
fn re_export_at_crate_root_works() {
let mut count = 0_usize;
ktav::parse_events("k: v\n", |_ev: ktav::ParseEvent<'_>| count += 1).unwrap();
assert!(count > 0);
}
#[test]
fn keywords_become_typed_events() {
let src = "a: null\nb: true\nc: false\n";
let events = collect(src);
assert_eq!(
events,
vec![
Owned::BeginObject,
Owned::Key("a".into()),
Owned::Null,
Owned::Key("b".into()),
Owned::Bool(true),
Owned::Key("c".into()),
Owned::Bool(false),
Owned::EndObject,
]
);
}