use ktav::{from_file, from_str, Error};
use serde::Deserialize;
use super::common::fixture;
#[test]
fn unclosed_object() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("x: {\n y: 1\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("Unclosed")),
"got: {:?}",
err
);
}
#[test]
fn unclosed_array() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: Vec<String>,
}
let err = from_str::<Cfg>("x: [\n a\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("Unclosed")),
"got: {:?}",
err
);
}
#[test]
fn mismatched_bracket() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("x: {\n]\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("does not match")),
"got: {:?}",
err
);
}
#[test]
fn stray_close_bracket() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("}\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("without matching")),
"got: {:?}",
err
);
}
#[test]
fn duplicate_key() {
#[derive(Debug, Deserialize)]
struct Cfg {
_port: u16,
}
let err = from_str::<Cfg>("port: 80\nport: 443\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("duplicate key")),
"got: {:?}",
err
);
}
#[test]
fn duplicate_key_inside_dotted_synthetic() {
#[derive(Debug, Deserialize)]
struct Cfg {
_db: serde::de::IgnoredAny,
}
let err = from_str::<Cfg>("db.host: a\ndb.host: b\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("duplicate key")),
"got: {:?}",
err
);
}
#[test]
fn dotted_prefix_after_scalar_at_same_name_conflicts() {
#[derive(Debug, Deserialize)]
struct Cfg {
_a: serde::de::IgnoredAny,
}
let err = from_str::<Cfg>("a: 1\na.b: 2\n").unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("conflict"), "got: {}", msg);
}
#[test]
fn scalar_after_dotted_prefix_conflicts() {
#[derive(Debug, Deserialize)]
struct Cfg {
_a: serde::de::IgnoredAny,
}
let err = from_str::<Cfg>("a.b: 1\na: 2\n").unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("conflict"), "got: {}", msg);
}
#[test]
fn interleaved_dotted_prefix_is_rejected_as_conflict() {
#[derive(Debug, Deserialize)]
struct Cfg {
_a: serde::de::IgnoredAny,
_b: serde::de::IgnoredAny,
}
let err = from_str::<Cfg>("a.x: 1\nb.y: 2\na.z: 3\n").unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("conflict"), "got: {}", msg);
}
#[test]
fn grouped_dotted_keys_with_multiple_prefixes_work() {
#[derive(Debug, Deserialize, PartialEq)]
struct A {
x: u32,
z: u32,
}
#[derive(Debug, Deserialize, PartialEq)]
struct B {
y: u32,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Cfg {
a: A,
b: B,
}
let cfg: Cfg = from_str("a.x: 1\na.z: 3\nb.y: 2\n").unwrap();
assert_eq!(cfg.a, A { x: 1, z: 3 });
assert_eq!(cfg.b, B { y: 2 });
}
#[test]
fn empty_key() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>(": value\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("Empty key")),
"got: {:?}",
err
);
}
#[test]
fn key_with_special_chars() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("[foo]: bar\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("Invalid key")),
"got: {:?}",
err
);
}
#[test]
fn line_without_colon_in_object() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("just-some-text\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("no ':'")),
"got: {:?}",
err
);
}
#[test]
fn inline_nonempty_object() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("x: { a: 1 }\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("inline non-empty object")),
"got: {:?}",
err
);
}
#[test]
fn inline_nonempty_array() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: Vec<String>,
}
let err = from_str::<Cfg>("x: [a b c]\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("inline non-empty array")),
"got: {:?}",
err
);
}
#[test]
fn bracket_value_without_double_colon() {
#[derive(Debug, Deserialize)]
struct Cfg {
_x: String,
}
let err = from_str::<Cfg>("x: [a-z]+\n").unwrap_err();
assert!(
matches!(err, Error::Syntax(ref m) if m.contains("inline")),
"got: {:?}",
err
);
}
#[test]
fn empty_input_yields_missing_required_field() {
#[derive(Debug, Deserialize)]
struct Cfg {
_port: u16,
}
let err = from_str::<Cfg>("").unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("port"), "got: {}", msg);
}
#[test]
fn from_file_missing_path_is_io_error() {
#[derive(Debug, Deserialize)]
struct Cfg {
_port: u16,
}
let err = from_file::<Cfg, _>(fixture("does_not_exist.conf")).unwrap_err();
assert!(matches!(err, Error::Io(_)));
}