use ktav::{Error, ErrorKind, Span};
fn span_of(text: &str) -> (ErrorKind, String) {
let err = ktav::parse(text).expect_err("expected parse failure");
match err {
Error::Structured(k) => {
let slice = k
.span()
.slice(text)
.map(|s| s.to_string())
.unwrap_or_default();
(k, slice)
}
other => panic!("expected Structured, got {other:?}"),
}
}
#[test]
fn span_missing_separator_space() {
let text = "anchor: ok\nkey:value\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::MissingSeparatorSpace { .. }));
assert_eq!(slice, "value");
}
#[test]
fn span_invalid_typed_scalar() {
let text = "port:i abc\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::InvalidTypedScalar { .. }));
assert_eq!(slice, " abc");
}
#[test]
fn span_duplicate_key() {
let text = "port: 80\nport: 443\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::DuplicateKey { .. }));
assert_eq!(slice, "port");
}
#[test]
fn span_key_path_conflict() {
let text = "db: 1\ndb.x: 2\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::KeyPathConflict { .. }));
assert_eq!(slice, "db.x");
}
#[test]
fn span_empty_key() {
let text = "anchor: ok\n: value\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::EmptyKey { .. }));
assert_eq!(slice, ":");
}
#[test]
fn span_invalid_key() {
let text = "a.: 1\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::InvalidKey { .. }));
assert_eq!(slice, "a.");
}
#[test]
fn span_unclosed_compound_object() {
let text = "obj: {\n a: 1\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::UnclosedCompound { .. }));
assert!(slice.starts_with('{'), "got: {slice:?}");
}
#[test]
fn span_unbalanced_bracket_stray() {
let text = "}\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::UnbalancedBracket { .. }));
assert_eq!(slice, "}");
}
#[test]
fn span_unbalanced_bracket_mismatch() {
let text = "obj: {\n]\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::UnbalancedBracket { .. }));
assert_eq!(slice, "]");
}
#[test]
fn span_inline_nonempty_compound() {
let text = "server: { host: 127.0.0.1 }\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::InlineNonEmptyCompound { .. }));
assert_eq!(slice, "server: { host: 127.0.0.1 }");
}
#[test]
fn span_missing_separator() {
let text = "anchor: ok\njust-some-text\n";
let (k, slice) = span_of(text);
assert!(matches!(k, ErrorKind::MissingSeparator { .. }));
assert_eq!(slice, "just-some-text");
}
#[test]
fn span_slice_returns_substring() {
let s = Span::new(2, 5);
assert_eq!(s.slice("abcdef"), Some("cde"));
}
#[test]
fn span_slice_out_of_bounds_returns_none() {
let s = Span::new(10, 20);
assert_eq!(s.slice("abcdef"), None);
}
#[test]
fn span_slice_non_char_boundary_returns_none() {
let text = "ыz";
let s = Span::new(1, 2);
assert_eq!(s.slice(text), None);
}
#[test]
fn span_line_col_first_line() {
let text = "abc\ndef\n";
let s = Span::new(0, 1);
assert_eq!(s.line_col(text), (1, 0));
let s = Span::new(2, 3);
assert_eq!(s.line_col(text), (1, 2));
}
#[test]
fn span_line_col_second_line() {
let text = "abc\ndef\n";
let s = Span::new(4, 5);
assert_eq!(s.line_col(text), (2, 0));
let s = Span::new(6, 7);
assert_eq!(s.line_col(text), (2, 2));
}
#[test]
fn span_line_col_with_multibyte_utf8_returns_byte_column() {
let text = "йx\nz";
let s = Span::new(2, 3);
assert_eq!(s.line_col(text), (1, 2));
let s = Span::new(3, 4);
assert_eq!(s.line_col(text), (1, 3));
let s = Span::new(4, 5);
assert_eq!(s.line_col(text), (2, 0));
}
#[test]
fn span_line_col_with_emoji_returns_byte_column() {
let text = "🦀X";
let s = Span::new(4, 5);
let (line, col) = s.line_col(text);
assert_eq!((line, col), (1, 4));
}