#![allow(
unused_comparisons,
clippy::approx_constant,
clippy::absurd_extreme_comparisons,
clippy::enum_variant_names,
clippy::upper_case_acronyms,
unused_results,
missing_docs
)]
use std::collections::HashMap;
use noyalib::*;
use serde::{Deserialize, Serialize};
#[test]
fn mapping_deserialize_from_yaml() {
let yaml = "a: 1\nb: 2\n";
let m: Mapping = from_str(yaml).unwrap();
assert_eq!(m.len(), 2);
assert_eq!(m.get("a").unwrap().as_i64(), Some(1));
}
#[test]
fn mapping_any_deserialize_from_yaml() {
let yaml = "a: 1\nb: 2\n";
let m: MappingAny = from_str(yaml).unwrap();
assert_eq!(m.len(), 2);
}
#[derive(Debug, Deserialize, PartialEq)]
enum MyEnum {
UnitVariant,
NewtypeVariant(i64),
TupleVariant(i64, String),
StructVariant { x: i64, y: String },
}
#[test]
fn tagged_value_deserialize_unit_variant() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!UnitVariant"),
Value::Null,
)));
let e: MyEnum = Deserialize::deserialize(&v).unwrap();
assert_eq!(e, MyEnum::UnitVariant);
}
#[test]
fn tagged_value_deserialize_newtype_variant() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!NewtypeVariant"),
Value::from(42),
)));
let e: MyEnum = Deserialize::deserialize(&v).unwrap();
assert_eq!(e, MyEnum::NewtypeVariant(42));
}
#[test]
fn tagged_value_deserialize_tuple_variant() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!TupleVariant"),
Value::Sequence(vec![Value::from(42), Value::from("hello")]),
)));
let e: MyEnum = Deserialize::deserialize(&v).unwrap();
assert_eq!(e, MyEnum::TupleVariant(42, "hello".to_string()));
}
#[test]
fn tagged_value_deserialize_struct_variant() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!StructVariant"),
Value::Mapping({
let mut m = Mapping::new();
m.insert("x", Value::from(42));
m.insert("y", Value::from("hello"));
m
}),
)));
let e: MyEnum = Deserialize::deserialize(&v).unwrap();
assert_eq!(
e,
MyEnum::StructVariant {
x: 42,
y: "hello".to_string()
}
);
}
#[test]
fn tagged_value_deserialize_any_as_map() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!mytag"),
Value::from("hello"),
)));
let map: HashMap<String, Value> = Deserialize::deserialize(&v).unwrap();
assert!(map.contains_key("!mytag"));
}
#[test]
fn apply_merge_scalar_in_merge_element() {
let mut v = Value::Mapping({
let mut m = Mapping::new();
m.insert("<<", Value::from("scalar_value"));
m
});
let err = v.apply_merge().unwrap_err();
assert!(format!("{err}").contains("scalar") || matches!(err, Error::ScalarInMergeElement));
}
#[test]
fn apply_merge_tagged_in_merge() {
let mut v = Value::Mapping({
let mut m = Mapping::new();
m.insert(
"<<",
Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!foo"),
Value::from("bar"),
))),
);
m
});
let err = v.apply_merge().unwrap_err();
assert!(matches!(err, Error::TaggedInMerge));
}
#[test]
fn apply_merge_sequence_in_merge_element() {
let mut v = Value::Mapping({
let mut m = Mapping::new();
m.insert(
"<<",
Value::Sequence(vec![Value::Sequence(vec![Value::from(1)])]),
);
m
});
let err = v.apply_merge().unwrap_err();
assert!(matches!(err, Error::SequenceInMergeElement));
}
#[test]
fn apply_merge_single_mapping_value() {
let mut v = Value::Mapping({
let mut m = Mapping::new();
let mut merge_src = Mapping::new();
merge_src.insert("a", Value::from(1));
m.insert("<<", Value::Mapping(merge_src));
m
});
v.apply_merge().unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn value_index_usize_on_tagged_sequence() {
let mut v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!seq"),
Value::Sequence(vec![Value::from(10), Value::from(20)]),
)));
v[0] = Value::from(99);
assert_eq!(v[0].as_i64(), Some(99));
}
#[test]
fn value_index_str_on_tagged_mapping() {
let mut v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!map"),
Value::Mapping(Mapping::new()),
)));
*"key".index_or_insert(&mut v) = Value::from("value");
assert_eq!("key".index_into(&v).unwrap().as_str(), Some("value"));
}
#[test]
fn value_index_str_on_null_creates_mapping() {
let mut v = Value::Null;
*"key".index_or_insert(&mut v) = Value::from("value");
assert_eq!(v["key"].as_str(), Some("value"));
}
#[test]
fn value_index_string_type() {
let mut m = Mapping::new();
m.insert("hello", Value::from(42));
let mut v = Value::Mapping(m);
let key = String::from("hello");
assert_eq!(key.clone().index_into(&v).unwrap().as_i64(), Some(42));
let key2 = String::from("hello");
*key2.index_into_mut(&mut v).unwrap() = Value::from(99);
assert_eq!(v["hello"].as_i64(), Some(99));
let key3 = String::from("new_key");
*key3.index_or_insert(&mut v) = Value::from(100);
assert_eq!(v["new_key"].as_i64(), Some(100));
}
#[test]
fn value_index_ref_string_type() {
let mut m = Mapping::new();
m.insert("hello", Value::from(42));
let mut v = Value::Mapping(m);
let key = String::from("hello");
assert_eq!((&key).index_into(&v).unwrap().as_i64(), Some(42));
*(&key).index_into_mut(&mut v).unwrap() = Value::from(99);
assert_eq!(v["hello"].as_i64(), Some(99));
*(&key).index_or_insert(&mut v) = Value::from(100);
assert_eq!(v["hello"].as_i64(), Some(100));
}
#[test]
fn value_index_ref_value_string() {
let mut m = Mapping::new();
m.insert("hello", Value::from(42));
let mut v = Value::Mapping(m);
let idx = Value::from("hello");
assert_eq!((&idx).index_into(&v).unwrap().as_i64(), Some(42));
*(&idx).index_into_mut(&mut v).unwrap() = Value::from(99);
assert_eq!(v["hello"].as_i64(), Some(99));
*(&idx).index_or_insert(&mut v) = Value::from(100);
assert_eq!(v["hello"].as_i64(), Some(100));
}
#[test]
fn value_index_ref_value_integer() {
let mut v = Value::Sequence(vec![Value::from(10), Value::from(20), Value::from(30)]);
let idx = Value::from(1);
assert_eq!((&idx).index_into(&v).unwrap().as_i64(), Some(20));
*(&idx).index_into_mut(&mut v).unwrap() = Value::from(99);
assert_eq!((&idx).index_into(&v).unwrap().as_i64(), Some(99));
*(&idx).index_or_insert(&mut v) = Value::from(88);
assert_eq!((&idx).index_into(&v).unwrap().as_i64(), Some(88));
}
#[test]
fn value_index_ref_value_other_returns_none() {
let mut m = Value::Mapping(Mapping::new());
let idx = Value::Bool(true);
assert!((&idx).index_into(&m).is_none());
assert!((&idx).index_into_mut(&mut m).is_none());
}
#[test]
#[should_panic(expected = "cannot index")]
fn value_index_ref_value_bool_panic_on_insert() {
let mut v = Value::Mapping(Mapping::new());
let idx = Value::Bool(true);
let _ = (&idx).index_or_insert(&mut v);
}
#[test]
fn value_deserialize_visit_string() {
let yaml = "hello world\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello world"));
}
#[test]
fn ref_value_deserialize_any_null() {
let v = Value::Null;
let result: () = from_value(&v).unwrap();
assert_eq!(result, ());
}
#[test]
fn ref_value_deserialize_any_bool() {
let v = Value::Bool(true);
let result: bool = from_value(&v).unwrap();
assert!(result);
}
#[test]
fn ref_value_deserialize_any_integer() {
let v = Value::from(42);
let result: i64 = from_value(&v).unwrap();
assert_eq!(result, 42);
}
#[test]
fn ref_value_deserialize_any_float() {
let v = Value::Number(Number::Float(3.14));
let result: f64 = from_value(&v).unwrap();
assert!((result - 3.14).abs() < f64::EPSILON);
}
#[test]
fn ref_value_deserialize_any_string() {
let v = Value::from("hello");
let result: String = from_value(&v).unwrap();
assert_eq!(result, "hello");
}
#[test]
fn ref_value_deserialize_any_sequence() {
let v = Value::Sequence(vec![Value::from(1), Value::from(2)]);
let result: Vec<i64> = from_value(&v).unwrap();
assert_eq!(result, vec![1, 2]);
}
#[test]
fn ref_value_deserialize_any_mapping() {
let v = Value::Mapping({
let mut m = Mapping::new();
m.insert("a", Value::from(1));
m
});
let result: HashMap<String, i64> = from_value(&v).unwrap();
assert_eq!(result["a"], 1);
}
#[test]
fn ref_value_deserialize_enum_string() {
#[derive(Debug, Deserialize, PartialEq)]
enum Simple {
Hello,
World,
}
let v = Value::from("Hello");
let result: Simple = from_value(&v).unwrap();
assert_eq!(result, Simple::Hello);
}
#[test]
fn ref_value_deserialize_seq_passthrough() {
let v = Value::from("not a sequence");
let result: Result<Vec<i64>> = from_value(&v);
assert!(result.is_err());
}
#[test]
fn ref_value_deserialize_map_passthrough() {
let v = Value::from("not a mapping");
let result: Result<HashMap<String, i64>> = from_value(&v);
assert!(result.is_err());
}
#[test]
fn ref_value_deserialize_struct_spanned() {
let yaml = "hello\n";
let v: Spanned<String> = from_str(yaml).unwrap();
assert_eq!(v.value, "hello");
}
#[test]
fn scanner_bom_at_start() {
let yaml = "\u{FEFF}key: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value"));
}
#[test]
fn scanner_single_quoted_multiline() {
let yaml = "key: 'line1\n line2'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2"));
}
#[test]
fn scanner_single_quoted_multiline_breaks() {
let yaml = "key: 'line1\n\n line2'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2"));
}
#[test]
fn scanner_single_quoted_escaped_quote() {
let yaml = "key: 'it''s'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("it's"));
}
#[test]
fn scanner_double_quoted_escapes() {
let yaml = r#"key: "\0\a\b\t\n\v\f\r\e \"\/ \\ \N \_ \L \P""#;
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains('\0'));
assert!(s.contains('\x07'));
assert!(s.contains('\x08'));
assert!(s.contains('\t'));
assert!(s.contains('\n'));
assert!(s.contains('\x0B'));
assert!(s.contains('\x0C'));
assert!(s.contains('\r'));
assert!(s.contains('\x1B'));
assert!(s.contains('"'));
assert!(s.contains('/'));
assert!(s.contains('\\'));
assert!(s.contains('\u{0085}'));
assert!(s.contains('\u{00A0}'));
assert!(s.contains('\u{2028}'));
assert!(s.contains('\u{2029}'));
}
#[test]
fn scanner_double_quoted_hex_escapes() {
let yaml = r#"key: "\x41\u0042\U00000043""#;
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("ABC"));
}
#[test]
fn scanner_double_quoted_multiline() {
let yaml = "key: \"line1\n line2\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2"));
}
#[test]
fn scanner_double_quoted_multiline_multiple_breaks() {
let yaml = "key: \"line1\n\n line2\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2"));
}
#[test]
fn scanner_double_quoted_line_escape() {
let yaml = "key: \"line1\\\n line2\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1line2"));
}
#[test]
fn scanner_block_scalar_literal_clip() {
let yaml = "key: |\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2\n"));
}
#[test]
fn scanner_block_scalar_literal_strip() {
let yaml = "key: |-\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2"));
}
#[test]
fn scanner_block_scalar_literal_keep() {
let yaml = "key: |+\n line1\n line2\n\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2\n\n"));
}
#[test]
fn scanner_block_scalar_with_indent_indicator() {
let yaml = "key: |2\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2\n"));
}
#[test]
fn scanner_block_scalar_folded() {
let yaml = "key: >\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2\n"));
}
#[test]
fn scanner_block_scalar_folded_strip() {
let yaml = "key: >-\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2"));
}
#[test]
fn scanner_block_scalar_folded_keep() {
let yaml = "key: >+\n line1\n line2\n\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2\n\n"));
}
#[test]
fn scanner_block_scalar_folded_with_more_indented() {
let yaml = "key: >\n line1\n indented\n line2\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("line1"));
assert!(s.contains("indented"));
assert!(s.contains("line2"));
}
#[test]
fn scanner_block_scalar_literal_utf8() {
let yaml = "key: |\n héllo wörld\n 日本語\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("héllo wörld"));
assert!(s.contains("日本語"));
}
#[test]
fn scanner_flow_collections() {
let yaml = "[1, {a: b}, 2]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_i64(), Some(1));
assert_eq!(v[2].as_i64(), Some(2));
assert_eq!(v[1]["a"].as_str(), Some("b"));
}
#[test]
fn scanner_flow_mapping() {
let yaml = "{a: [1, 2], b: 3}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["b"].as_i64(), Some(3));
assert_eq!(v["a"][0].as_i64(), Some(1));
}
#[test]
fn scanner_crlf_line_endings() {
let yaml = "key: value\r\nother: data\r\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value"));
assert_eq!(v["other"].as_str(), Some("data"));
}
#[test]
fn scanner_unterminated_single_quote() {
let yaml = "key: 'unterminated";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_unterminated_double_quote() {
let yaml = "key: \"unterminated";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_tab_indentation_error() {
let yaml = "key: value\n\tindented: bad\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn scanner_unknown_escape() {
let yaml = r#"key: "\z""#;
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_plain_scalar_multiline() {
let yaml = "key:\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2"));
}
#[test]
fn scanner_plain_scalar_with_multiple_breaks() {
let yaml = "key:\n line1\n\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\nline2"));
}
#[test]
fn scanner_roll_indent_flow_return() {
let yaml = "{a: {b: c}}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"]["b"].as_str(), Some("c"));
}
#[test]
fn scanner_fetch_value_block_mapping_init() {
let yaml = "? key\n: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value"));
}
#[test]
fn events_multi_document() {
let yaml = "---\na: 1\n...\n---\nb: 2\n...\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert_eq!(docs.len(), 2);
assert_eq!(docs[0]["a"].as_i64(), Some(1));
assert_eq!(docs[1]["b"].as_i64(), Some(2));
}
#[test]
fn events_explicit_key_syntax() {
let yaml = "? explicit_key\n: explicit_value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["explicit_key"].as_str(), Some("explicit_value"));
}
#[test]
fn events_indentless_sequence() {
let yaml = "key:\n - item1\n - item2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"][0].as_str(), Some("item1"));
assert_eq!(v["key"][1].as_str(), Some("item2"));
}
#[test]
fn events_flow_sequence_implicit_mapping() {
let yaml = "[a, b, c]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_str(), Some("a"));
assert_eq!(v[1].as_str(), Some("b"));
assert_eq!(v[2].as_str(), Some("c"));
}
#[test]
fn events_empty_block_seq_entries() {
let yaml = "- \n- value\n";
let v: Value = from_str(yaml).unwrap();
assert!(v[0].is_null());
assert_eq!(v[1].as_str(), Some("value"));
}
#[test]
fn events_flow_mapping_empty_values() {
let yaml = "{? a, b: c}\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_str(), Some("c"));
}
#[test]
fn events_anchor_tag_combination() {
let yaml = "&anc !!custom value\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_tagged());
}
#[test]
fn events_tag_then_anchor() {
let yaml = "!!custom &anc value\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_tagged());
}
#[test]
fn events_document_content_empty() {
let yaml = "---\n...\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_null());
}
#[test]
fn events_block_mapping_empty_value() {
let yaml = "a:\nb: 2\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn events_flow_sequence_entry_mapping_value() {
let yaml = "[a: b, c]\n";
let v: Value = from_str(yaml).unwrap();
assert!(v[0].is_mapping());
}
#[test]
fn events_flow_sequence_entry_mapping_empty_value() {
let yaml = "[a:, b]\n";
let v: Value = from_str(yaml).unwrap();
assert!(v[0].is_mapping());
}
#[test]
fn events_flow_mapping_complex_key() {
let yaml = "{a: b, c: d}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_str(), Some("b"));
assert_eq!(v["c"].as_str(), Some("d"));
}
#[test]
fn loader_multi_document() {
let yaml = "doc1\n---\ndoc2\n";
let docs = load_all(yaml).unwrap();
assert!(docs.len() >= 2); }
#[test]
fn loader_anchored_sequence() {
let yaml = "a: &seq\n - 1\n - 2\nb: *seq\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"][0].as_i64(), Some(1));
assert_eq!(v["b"][0].as_i64(), Some(1));
}
#[test]
fn loader_anchored_mapping() {
let yaml = "a: &map\n x: 1\n y: 2\nb: *map\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"]["x"].as_i64(), Some(1));
assert_eq!(v["b"]["x"].as_i64(), Some(1));
}
#[test]
fn loader_merge_keys() {
let yaml = "defaults: &defaults\n x: 1\n y: 2\nresult:\n <<: *defaults\n z: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["result"]["x"].as_i64(), Some(1));
assert_eq!(v["result"]["z"].as_i64(), Some(3));
}
#[test]
fn loader_merge_keys_sequence() {
let yaml = "a: &a\n x: 1\nb: &b\n y: 2\nc:\n <<: [*a, *b]\n z: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["c"]["x"].as_i64(), Some(1));
assert_eq!(v["c"]["y"].as_i64(), Some(2));
assert_eq!(v["c"]["z"].as_i64(), Some(3));
}
#[test]
fn loader_duplicate_key_first() {
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let yaml = "a: 1\na: 2\n";
let v: Value = from_str_with_config(yaml, &config).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn loader_tagged_scalars() {
let yaml_int = "!!int 42\n";
let v: Value = from_str(yaml_int).unwrap();
assert_eq!(v.as_i64(), Some(42));
let yaml_bool = "!!bool true\n";
let v: Value = from_str(yaml_bool).unwrap();
assert_eq!(v.as_bool(), Some(true));
let yaml_float = "!!float 3.14\n";
let v: Value = from_str(yaml_float).unwrap();
assert!((v.as_f64().unwrap() - 3.14).abs() < f64::EPSILON);
let yaml_null = "!!null ~\n";
let v: Value = from_str(yaml_null).unwrap();
assert!(v.is_null());
let yaml_str = "!!str 42\n";
let v: Value = from_str(yaml_str).unwrap();
assert_eq!(v.as_str(), Some("42"));
}
#[test]
fn loader_custom_tag() {
let yaml = "!!custom hello\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_tagged());
}
#[test]
fn loader_hex_octal_integers() {
let yaml_hex = "0xFF\n";
let v: Value = from_str(yaml_hex).unwrap();
assert_eq!(v.as_i64(), Some(255));
let yaml_oct = "0o77\n";
let v: Value = from_str(yaml_oct).unwrap();
assert_eq!(v.as_i64(), Some(63));
}
#[test]
fn loader_large_integer_overflow_to_float() {
let yaml = "99999999999999999999\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_f64().is_some());
}
#[test]
fn loader_value_to_key_types() {
let yaml = "true: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["true"].as_str(), Some("value"));
let yaml = "null: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["null"].as_str(), Some("value"));
let yaml = "3.14: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["3.14"].as_str(), Some("value"));
let yaml = "42: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["42"].as_str(), Some("value"));
}
#[test]
fn de_deserialize_identifier() {
#[derive(Debug, Deserialize, PartialEq)]
struct Wrapper {
field: String,
}
let yaml = "field: hello\n";
let w: Wrapper = from_str(yaml).unwrap();
assert_eq!(w.field, "hello");
}
#[test]
fn de_spanned_map_access_all_fields() {
#[derive(Debug, Deserialize)]
struct Config {
name: Spanned<String>,
value: Spanned<i64>,
}
let yaml = "name: hello\nvalue: 42\n";
let config: Config = from_str(yaml).unwrap();
assert_eq!(config.name.value, "hello");
assert_eq!(config.value.value, 42);
}
#[test]
fn spanned_string_from_yaml() {
let yaml = "hello\n";
let v: Spanned<String> = from_str(yaml).unwrap();
assert_eq!(v.value, "hello");
}
#[test]
fn spanned_integer_from_yaml() {
let yaml = "42\n";
let v: Spanned<i64> = from_str(yaml).unwrap();
assert_eq!(v.value, 42);
}
#[test]
fn ser_string_needs_single_quotes() {
let v = Value::from("true");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
let v = Value::from("null");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
let v = Value::from("42");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
}
#[test]
fn ser_string_needs_double_quotes() {
let v = Value::from("hello\nworld");
let s = to_string(&v).unwrap();
assert!(s.contains('"') || s.contains('|') || s.contains('>'));
}
#[test]
fn ser_looks_like_number() {
let v = Value::from(".5");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
let v = Value::from(".inf");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
let v = Value::from("-.inf");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
let v = Value::from(".nan");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
}
#[test]
fn ser_tagged_value() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!custom"),
Value::from("hello"),
)));
let s = to_string(&v).unwrap();
assert!(s.contains("!custom"));
assert!(s.contains("hello"));
}
#[test]
fn ser_literal_block() {
use noyalib::fmt::LitString;
#[derive(Serialize)]
struct Doc {
script: LitString,
}
let doc = Doc {
script: LitString::from("line1\nline2\n".to_string()),
};
let s = to_string(&doc).unwrap();
assert!(s.contains('|'));
}
#[test]
fn ser_folded_block() {
use noyalib::fmt::FoldString;
#[derive(Serialize)]
struct Doc {
text: FoldString,
}
let doc = Doc {
text: FoldString::from("line1\nline2\n".to_string()),
};
let s = to_string(&doc).unwrap();
assert!(s.contains('>'));
}
#[test]
fn ser_flow_sequence() {
use noyalib::fmt::FlowSeq;
#[derive(Serialize)]
struct Doc {
items: FlowSeq<Vec<i32>>,
}
let doc = Doc {
items: FlowSeq(vec![1, 2, 3]),
};
let s = to_string(&doc).unwrap();
assert!(s.contains('['));
assert!(s.contains(']'));
}
#[test]
fn ser_flow_mapping() {
use noyalib::fmt::FlowMap;
#[derive(Serialize)]
struct Doc {
map: FlowMap<std::collections::BTreeMap<String, i32>>,
}
let mut m = std::collections::BTreeMap::new();
m.insert("a".to_string(), 1);
m.insert("b".to_string(), 2);
let doc = Doc { map: FlowMap(m) };
let s = to_string(&doc).unwrap();
assert!(s.contains('{'));
assert!(s.contains('}'));
}
#[test]
fn ser_string_trailing_space() {
let v = Value::from("hello ");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
}
#[test]
fn ser_string_first_char_quote() {
let v = Value::from("# comment-like");
let s = to_string(&v).unwrap();
assert!(s.contains('\'') || s.contains('"'));
}
#[test]
fn path_alias_parent_and_depth() {
let root = Path::Root;
let alias = Path::Alias { parent: &root };
assert_eq!(alias.parent(), Some(&Path::Root));
assert_eq!(alias.depth(), 1);
}
#[test]
fn path_unknown_parent_and_depth() {
let root = Path::Root;
let unknown = Path::Unknown { parent: &root };
assert_eq!(unknown.parent(), Some(&Path::Root));
assert_eq!(unknown.depth(), 1);
}
#[test]
fn path_seq_parent_and_depth() {
let root = Path::Root;
let seq = Path::Seq {
parent: &root,
index: 0,
};
assert_eq!(seq.parent(), Some(&Path::Root));
assert_eq!(seq.depth(), 1);
}
#[test]
fn fmt_commented_roundtrip() {
use noyalib::fmt::Commented;
#[derive(Serialize)]
struct Doc {
key: Commented<String>,
}
let doc = Doc {
key: Commented::new("hello".to_string(), "my comment"),
};
let s = to_string(&doc).unwrap();
assert!(s.contains("hello"));
assert!(s.contains("# my comment"));
#[derive(Deserialize)]
struct DocIn {
key: Commented<String>,
}
let parsed: DocIn = from_str("key: hello\n").unwrap();
assert_eq!(*parsed.key, "hello");
}
#[test]
fn singleton_map_recursive_tagged_value() {
use noyalib::with::singleton_map_recursive;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Action {
Start { delay: u32 },
Stop,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Task {
#[serde(with = "singleton_map_recursive")]
action: Action,
}
let task = Task {
action: Action::Start { delay: 5 },
};
let yaml = to_string(&task).unwrap();
let parsed: Task = from_str(&yaml).unwrap();
assert_eq!(parsed, task);
}
#[test]
fn singleton_map_with_deserialize_transform() {
use noyalib::with::singleton_map_with;
#[derive(Debug, Deserialize, PartialEq)]
enum MyAction {
START,
STOP,
}
let yaml = "start: ~\n";
let val: Value = from_str(yaml).unwrap();
let result: MyAction =
singleton_map_with::deserialize_with(Deserializer::new(&val), |s| s.to_uppercase())
.unwrap();
assert_eq!(result, MyAction::START);
}
#[test]
fn singleton_map_with_serialize_branches() {
use noyalib::with::singleton_map_with;
#[derive(Debug, Serialize, PartialEq)]
enum Status {
Active,
WithData { count: u32 },
}
let unit_val: Value =
singleton_map_with::serialize_with(&Status::Active, Serializer, |s| s.to_lowercase())
.unwrap();
assert!(unit_val.is_mapping());
let struct_val: Value =
singleton_map_with::serialize_with(&Status::WithData { count: 5 }, Serializer, |s| {
s.to_lowercase()
})
.unwrap();
assert!(struct_val.is_mapping());
}
#[test]
fn scanner_tag_secondary_handle() {
let yaml = "!!str 42\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("42"));
}
#[test]
fn scanner_tag_verbatim() {
let yaml = "!<my-custom-tag> hello\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_tagged());
}
#[test]
fn scanner_tag_primary() {
let yaml = "!custom hello\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_tagged());
}
#[test]
fn scanner_anchor_and_alias() {
let yaml = "a: &anchor value\nb: *anchor\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_str(), Some("value"));
assert_eq!(v["b"].as_str(), Some("value"));
}
#[test]
fn scanner_double_quoted_trailing_whitespace_before_break() {
let yaml = "key: \"word \n next\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("word"));
assert!(s.contains("next"));
}
#[test]
fn scanner_block_scalar_empty_lines() {
let yaml = "key: |\n line1\n\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1\n\nline2\n"));
}
#[test]
fn scanner_block_scalar_comment_after_indicator() {
let yaml = "key: | # comment\n content\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("content\n"));
}
#[test]
fn scanner_block_scalar_chomping_then_indent() {
let yaml = "key: |+2\n ab\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("ab\n"));
}
#[test]
fn scanner_block_scalar_indent_then_chomping() {
let yaml = "key: |2+\n ab\n\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("ab\n\n"));
}
#[test]
fn scanner_hex_escape_invalid() {
let yaml = r#"key: "\xZZ""#;
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_special_floats() {
let yaml = ".inf\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_f64(), Some(f64::INFINITY));
let yaml = "-.inf\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_f64(), Some(f64::NEG_INFINITY));
let yaml = ".nan\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_f64().unwrap().is_nan());
}
#[test]
fn loader_tagged_bool_resolution() {
let yaml = "!!bool false\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_bool(), Some(false));
}
#[test]
fn ser_space_after() {
use noyalib::fmt::SpaceAfter;
#[derive(Serialize)]
struct Doc {
section: SpaceAfter<String>,
}
let doc = Doc {
section: SpaceAfter(String::from("hello")),
};
let s = to_string(&doc).unwrap();
assert!(s.contains("hello"));
}
#[test]
fn ref_value_deserialize_tagged_any() {
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!mytag"),
Value::from("inner"),
)));
let result: HashMap<String, Value> = Deserialize::deserialize(&tagged).unwrap();
assert!(result.contains_key("!mytag"));
}
#[test]
fn ref_value_deserialize_enum_tagged() {
#[derive(Debug, Deserialize, PartialEq)]
enum Color {
Red,
Blue,
}
let v = Value::Tagged(Box::new(TaggedValue::new(Tag::new("!Red"), Value::Null)));
let c: Color = Deserialize::deserialize(&v).unwrap();
assert_eq!(c, Color::Red);
}
#[test]
fn ref_value_deserialize_enum_non_tagged_non_string() {
#[derive(Debug, Deserialize, PartialEq)]
enum Thing {
Value(i64),
}
let v = Value::from(42);
let result: Result<Thing> = from_value(&v);
assert!(result.is_err());
}
#[test]
fn loader_estimate_value_covers_tagged() {
let yaml = "a: &a !!custom val\nb: *a\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0]["b"].is_tagged());
}
#[test]
fn events_flow_mapping_value_empty() {
let yaml = "{a: , b: 2}\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn events_flow_mapping_implicit_key() {
let yaml = "{a: 1}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn scan_error_display() {
let yaml = r#"key: "\z""#;
let err = from_str::<Value>(yaml).unwrap_err();
let msg = format!("{err}");
assert!(!msg.is_empty());
}
#[test]
fn loader_duplicate_key_last_policy() {
let yaml = "a: 1\na: 2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(2));
}
#[test]
fn scanner_block_scalar_folded_leading_blank() {
let yaml = "key: >\n normal1\n indented\n normal2\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("normal1"));
assert!(s.contains("indented"));
assert!(s.contains("normal2"));
}
#[test]
fn scanner_block_scalar_folded_multiple_breaks() {
let yaml = "key: >\n line1\n\n\n line2\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("line1\n\nline2"));
}
#[test]
fn ref_value_deserialize_struct_normal() {
#[derive(Debug, Deserialize, PartialEq)]
struct Point {
x: i64,
y: i64,
}
let v = Value::Mapping({
let mut m = Mapping::new();
m.insert("x", Value::from(1));
m.insert("y", Value::from(2));
m
});
let p: Point = from_value(&v).unwrap();
assert_eq!(p, Point { x: 1, y: 2 });
}
#[test]
fn ref_value_into_deserializer() {
use serde::de::IntoDeserializer;
let v = Value::from(42);
let de: &Value = (&v).into_deserializer();
let result: i64 = from_value(de).unwrap();
assert_eq!(result, 42);
}
#[test]
fn load_all_as_typed() {
let yaml = "---\n42\n---\n99\n";
let values: Vec<i64> = load_all_as(yaml).unwrap();
assert_eq!(values, vec![42, 99]);
}
#[test]
fn try_load_all_returns_iterator() {
let yaml = "---\n42\n---\n99\n";
let iter = try_load_all(yaml).unwrap();
assert_eq!(iter.len(), 2);
let results: Vec<Value> = iter.filter_map(|r| r.ok()).collect();
assert_eq!(results.len(), 2);
}
#[test]
fn scanner_skip_to_next_token_flow() {
let yaml = "[1,\t2, 3]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_i64(), Some(1));
assert_eq!(v[1].as_i64(), Some(2));
assert_eq!(v[2].as_i64(), Some(3));
}
#[test]
fn mapping_visitor_expecting_error() {
let result: std::result::Result<Mapping, _> = serde_json::from_str("42");
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("a YAML mapping") || err.contains("invalid type"));
}
#[test]
fn mapping_any_visitor_expecting_error() {
let result: std::result::Result<MappingAny, _> = serde_json::from_str("42");
assert!(result.is_err());
}
#[test]
fn tagged_value_visitor_expecting_error() {
let result: std::result::Result<TaggedValue, _> = serde_json::from_str("42");
assert!(result.is_err());
}
#[test]
fn tagged_value_visitor_from_json_map() {
let result: TaggedValue = serde_json::from_str(r#"{"mytag": "val"}"#).unwrap();
assert_eq!(result.tag().as_str(), "mytag");
}
#[test]
fn usize_index_or_insert_through_tagged() {
let mut v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("t"),
Value::Sequence(vec![Value::Null]),
)));
v[0] = Value::from(1);
match &v {
Value::Tagged(t) => {
assert_eq!(t.value()[0].as_i64(), Some(1));
}
_ => panic!("expected tagged"),
}
}
#[test]
#[should_panic(expected = "null")]
fn value_type_name_null() {
let mut v = Value::Null;
0usize.index_or_insert(&mut v);
}
#[test]
#[should_panic(expected = "boolean")]
fn value_type_name_bool() {
let mut v = Value::Bool(true);
0usize.index_or_insert(&mut v);
}
#[test]
#[should_panic(expected = "number")]
fn value_type_name_number() {
let mut v = Value::from(42);
0usize.index_or_insert(&mut v);
}
#[test]
#[should_panic(expected = "string")]
fn value_type_name_string_usize_index() {
let mut v = Value::from("hello");
0usize.index_or_insert(&mut v);
}
#[test]
fn value_deserialize_from_json_string() {
let v: Value = serde_json::from_str(r#""hello""#).unwrap();
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn value_deserialize_from_json_various() {
let v: Value = serde_json::from_str("true").unwrap();
assert_eq!(v.as_bool(), Some(true));
let v: Value = serde_json::from_str("null").unwrap();
assert!(v.is_null());
let v: Value = serde_json::from_str("[1,2]").unwrap();
assert_eq!(v[0].as_i64(), Some(1));
let v: Value = serde_json::from_str(r#"{"a":1}"#).unwrap();
assert!(v["a"].as_i64().is_some());
}
#[test]
fn ref_value_deserialize_any_all_types() {
use serde::Deserialize;
let v = Value::Null;
let result: Value = Value::deserialize(&v).unwrap();
assert!(result.is_null());
let v = Value::Bool(false);
let result: Value = Value::deserialize(&v).unwrap();
assert_eq!(result.as_bool(), Some(false));
let v = Value::from(99i64);
let result: Value = Value::deserialize(&v).unwrap();
assert_eq!(result.as_i64(), Some(99));
let v = Value::from(3.14f64);
let result: Value = Value::deserialize(&v).unwrap();
assert!(result.as_f64().is_some());
let v = Value::from("test");
let result: Value = Value::deserialize(&v).unwrap();
assert_eq!(result.as_str(), Some("test"));
let v = Value::Sequence(vec![Value::from(1), Value::from(2)]);
let result: Value = Value::deserialize(&v).unwrap();
assert_eq!(result[0].as_i64(), Some(1));
let mut m = Mapping::new();
let _ = m.insert("k".to_string(), Value::from(10));
let v = Value::Mapping(m);
let result: Value = Value::deserialize(&v).unwrap();
assert_eq!(result["k"].as_i64(), Some(10));
}
#[test]
fn ref_value_deserialize_enum_string_variant() {
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
enum Color {
Red,
Blue,
}
let v = Value::from("Red");
let result: Color = Color::deserialize(&v).unwrap();
assert_eq!(result, Color::Red);
}
#[test]
fn ref_value_deserialize_seq_fallback() {
use serde::Deserialize;
let v = Value::from(42);
let result: std::result::Result<Vec<i64>, _> = Vec::<i64>::deserialize(&v);
assert!(result.is_err());
}
#[test]
fn ref_value_deserialize_struct_spanned_via_ref() {
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
port: Spanned<u16>,
}
let yaml = "port: 8080\n";
let config: Config = from_str(yaml).unwrap();
assert_eq!(*config.port, 8080);
}
#[test]
fn ref_value_deserialize_struct_spanned_from_value() {
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
port: Spanned<u16>,
}
let mut m = Mapping::new();
let _ = m.insert("port".to_string(), Value::from(8080));
let v = Value::Mapping(m);
let config: Config = from_value(&v).unwrap();
assert_eq!(*config.port, 8080);
}
#[test]
fn value_seq_access_empty() {
use serde::Deserialize;
let v = Value::Sequence(vec![]);
let result: Vec<i64> = Vec::<i64>::deserialize(&v).unwrap();
assert!(result.is_empty());
}
#[test]
fn value_map_access_through_tagged() {
use serde::Deserialize;
let mut m = Mapping::new();
let _ = m.insert("x".to_string(), Value::from(10));
let tagged = TaggedValue::new(Tag::new("point"), Value::Mapping(m));
let v = Value::Tagged(Box::new(tagged));
let result: Value = Value::deserialize(&v).unwrap();
assert!(matches!(result, Value::Mapping(_)));
}
#[test]
fn scan_error_display_format() {
let result: Result<Value> = from_str(":\n :\n :\nbad: [");
assert!(result.is_err());
let err_str = result.unwrap_err().to_string();
assert!(!err_str.is_empty());
}
#[test]
fn scanner_empty_input() {
let result: Result<Value> = from_str("");
assert_eq!(result.unwrap(), Value::Null);
}
#[test]
fn scanner_whitespace_only() {
let result: Result<Value> = from_str(" ");
assert_eq!(result.unwrap(), Value::Null);
}
#[test]
fn scanner_bom_utf8() {
let yaml = "\u{FEFF}key: val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("val"));
}
#[test]
fn scanner_explicit_key_in_flow() {
let yaml = "? a\n: 1\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn scanner_colon_before_flow_indicators() {
let yaml = "{a: 1, b: [2, 3]}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn scanner_dash_in_flow_context() {
let yaml = "[1, -2, 3]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_i64(), Some(1));
assert_eq!(v[1].as_i64(), Some(-2));
}
#[test]
fn scanner_fetch_key_error() {
let result: Result<Value> = from_str(" ? key\n");
let _ = result;
}
#[test]
fn scanner_complex_key_value() {
let yaml = "? key1\n: val1\n? key2\n: val2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key1"].as_str(), Some("val1"));
assert_eq!(v["key2"].as_str(), Some("val2"));
}
#[test]
fn scanner_anchor_name_limit() {
let long_name = "a".repeat(1030);
let yaml = format!("&{} val\n", long_name);
let result: Result<Value> = from_str(&yaml);
assert!(result.is_err());
}
#[test]
fn scanner_single_quoted_unterminated() {
let result: Result<Value> = from_str("'unterminated");
assert!(result.is_err());
}
#[test]
fn scanner_single_quoted_line_folding_multi_break() {
let yaml = "'first\n\n\nsecond'\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("first"));
assert!(s.contains("second"));
}
#[test]
fn scanner_single_quoted_whitespace_between_words() {
let yaml = "'word1 word2'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("word1 word2"));
}
#[test]
fn scanner_double_quoted_unterminated() {
let result: Result<Value> = from_str("\"unterminated");
assert!(result.is_err());
}
#[test]
fn scanner_double_quoted_close_after_break() {
let yaml = "\"word\n \"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("word"));
}
#[test]
fn scanner_double_quoted_escape_after_break() {
let yaml = "\"word\n \\nmore\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("word"));
}
#[test]
fn scanner_double_quoted_multi_line_breaks() {
let yaml = "\"first\n\n\nsecond\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("first"));
assert!(s.contains("second"));
}
#[test]
fn scanner_double_quoted_whitespace_buffering() {
let yaml = "\"word1 word2\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("word1 word2"));
}
#[test]
fn scanner_hex_escape_invalid_char() {
let result: Result<Value> = from_str("\"\\xGG\"\n");
assert!(result.is_err());
}
#[test]
fn scanner_unicode_escape_surrogate() {
let result: Result<Value> = from_str("\"\\uD800\"\n");
assert!(result.is_err());
}
#[test]
fn scanner_block_scalar_explicit_indent_indicator() {
let yaml = "|2\n text\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("text\n"));
}
#[test]
fn scanner_block_scalar_content_end_at_lower_indent() {
let yaml = "data: |\n text\nnot_indented: val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["data"].as_str(), Some("text\n"));
assert_eq!(v["not_indented"].as_str(), Some("val"));
}
#[test]
fn scanner_block_scalar_utf8_content() {
let yaml = "|\n caf\u{00e9}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("caf\u{00e9}\n"));
}
#[test]
fn scanner_double_quoted_cr_lf_escape() {
let yaml = "\"word\\\r\n more\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("wordmore"));
}
#[test]
fn scanner_double_quoted_eof_in_escape() {
let result: Result<Value> = from_str("\"\\");
assert!(result.is_err());
}
#[test]
fn scanner_empty_plain_scalar_error() {
let result: Result<Value> = from_str("}");
assert!(result.is_err());
}
#[test]
fn scanner_plain_scalar_crlf_multiline() {
let yaml = "key:\r\n val\r\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("val"));
}
#[test]
fn scanner_plain_scalar_with_colon_space() {
let yaml = "{key: value}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value"));
}
#[test]
fn events_explicit_document_start() {
let yaml = "---\na: 1\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn events_document_content_stream_end() {
let yaml = "---\n...\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_null());
}
#[test]
fn events_tag_after_anchor() {
let yaml = "&anc !!str val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("val"));
}
#[test]
fn events_flow_in_parse_node() {
let yaml = "seq: [1, 2]\nmap: {a: b}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["seq"][0].as_i64(), Some(1));
assert_eq!(v["map"]["a"].as_str(), Some("b"));
}
#[test]
fn events_anchor_without_value() {
let yaml = "&anc\nkey: val\n";
let result: Result<Value> = from_str(yaml);
let _ = result; }
#[test]
fn events_indentless_sequence_fallback() {
let yaml = "key:\n - a\n - b\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"][0].as_str(), Some("a"));
assert_eq!(v["key"][1].as_str(), Some("b"));
}
#[test]
fn events_block_mapping_explicit_key_empty_value() {
let yaml = "? key1\n? key2\n: val2\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["key1"].is_null());
assert_eq!(v["key2"].as_str(), Some("val2"));
}
#[test]
fn events_block_mapping_value_skip() {
let yaml = "? key\n: val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("val"));
}
#[test]
fn events_flow_sequence_with_mapping() {
let yaml = "[{a: 1}, {b: 2}]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0]["a"].as_i64(), Some(1));
assert_eq!(v[1]["b"].as_i64(), Some(2));
}
#[test]
fn events_flow_sequence_implicit_map_entry() {
let yaml = "[a: b]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0]["a"].as_str(), Some("b"));
}
#[test]
fn events_flow_mapping_multiple_entries() {
let yaml = "{a: 1, b: 2, c: 3}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["b"].as_i64(), Some(2));
assert_eq!(v["c"].as_i64(), Some(3));
}
#[test]
fn events_flow_mapping_key_comma() {
let yaml = "{? a, b: 2}\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn loader_multi_doc_stream() {
let yaml = "---\na: 1\n---\nb: 2\n";
let docs = load_all(yaml).unwrap();
assert_eq!(docs.len(), 2);
}
#[test]
fn loader_sequence_mapping_lifecycle() {
let yaml = "items:\n - name: a\n val: 1\n - name: b\n val: 2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["items"][0]["name"].as_str(), Some("a"));
assert_eq!(v["items"][1]["val"].as_i64(), Some(2));
}
#[test]
fn loader_merge_key_with_scalar_in_sequence() {
let yaml = "defaults: &defaults\n a: 1\nresult:\n <<: [*defaults, badval]\n b: 2\n";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_duplicate_key_error_policy() {
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::Error);
let yaml = "a: 1\na: 2\n";
let result: Result<Value> = from_str_with_config(yaml, &config);
assert!(result.is_err());
}
#[test]
fn loader_complex_nested_yaml() {
let yaml = "root:\n child1:\n - item1\n - item2\n child2:\n key: val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["root"]["child1"][0].as_str(), Some("item1"));
assert_eq!(v["root"]["child2"]["key"].as_str(), Some("val"));
}
#[test]
fn loader_merge_key_single_mapping() {
let yaml = "defaults: &defaults\n a: 1\n b: 2\nresult:\n <<: *defaults\n c: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["result"]["a"].as_i64(), Some(1));
assert_eq!(v["result"]["c"].as_i64(), Some(3));
}
#[test]
fn loader_merge_key_sequence_of_mappings() {
let yaml = "d1: &d1\n a: 1\nd2: &d2\n b: 2\nresult:\n <<: [*d1, *d2]\n c: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["result"]["a"].as_i64(), Some(1));
assert_eq!(v["result"]["b"].as_i64(), Some(2));
assert_eq!(v["result"]["c"].as_i64(), Some(3));
}
#[test]
fn loader_duplicate_key_first_policy() {
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let yaml = "a: 1\na: 2\n";
let v: Value = from_str_with_config(yaml, &config).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn loader_tagged_int() {
let yaml = "!!int 42\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_i64(), Some(42));
}
#[test]
fn loader_tagged_float() {
let yaml = "!!float 3.14\n";
let v: Value = from_str(yaml).unwrap();
assert!((v.as_f64().unwrap() - 3.14).abs() < 0.001);
}
#[test]
fn loader_tag_primary_empty_suffix() {
let yaml = "! val\n";
let v: Value = from_str(yaml).unwrap();
assert!(matches!(v, Value::Tagged(_)), "tag is preserved");
assert_eq!(v.untag_ref().as_str(), Some("val"));
}
#[test]
fn loader_custom_tag_with_inner_resolution() {
let yaml = "!mytag 42\n";
let v: Value = from_str(yaml).unwrap();
let Value::Tagged(t) = &v else {
panic!("expected Tagged, got {v:?}");
};
assert_eq!(t.tag().as_str(), "!mytag");
assert_eq!(t.value().as_str(), Some("42"));
}
#[test]
fn loader_alias_expansion() {
let yaml = "big: &big\n a: 1\n b: 2\nref1: *big\nref2: *big\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["ref1"]["a"].as_i64(), Some(1));
assert_eq!(v["ref2"]["b"].as_i64(), Some(2));
}
#[test]
fn loader_hex_octal() {
let yaml = "hex: 0xFF\noct: 0o77\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["hex"].as_i64(), Some(255));
assert_eq!(v["oct"].as_i64(), Some(63));
}
#[test]
fn loader_large_int_overflow_float() {
let yaml = "99999999999999999999\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_f64().is_some());
}
#[test]
fn de_spanned_all_field_states() {
#[derive(Debug, Deserialize)]
struct S {
name: Spanned<String>,
count: Spanned<i64>,
}
let yaml = "name: hello\ncount: 42\n";
let s: S = from_str(yaml).unwrap();
assert_eq!(*s.name, "hello");
assert_eq!(*s.count, 42);
let _ = s.name.start.index();
let _ = s.name.start.line();
let _ = s.name.start.column();
}
#[test]
fn spanned_bool_from_yaml() {
#[derive(Debug, Deserialize)]
struct S {
flag: Spanned<bool>,
}
let yaml = "flag: true\n";
let s: S = from_str(yaml).unwrap();
assert!(*s.flag);
}
#[test]
fn ser_looks_like_number_dot_five() {
let yaml = to_string(&Value::from(".5")).unwrap();
assert!(yaml.contains('"') || yaml.contains('\''));
}
#[test]
fn ser_reserved_words_quoted() {
let yaml = to_string(&Value::from("true")).unwrap();
assert!(yaml.contains('"') || yaml.contains('\''));
let yaml = to_string(&Value::from("null")).unwrap();
assert!(yaml.contains('"') || yaml.contains('\''));
let yaml = to_string(&Value::from("~")).unwrap();
assert!(yaml.contains('"') || yaml.contains('\''));
}
#[test]
fn ser_first_char_single_quote() {
let yaml = to_string(&Value::from("'hello")).unwrap();
assert!(yaml.contains('"'));
}
#[test]
fn ser_commented_value_in_sequence() {
let mut inner = Mapping::new();
let _ = inner.insert("a".to_string(), Value::from(1));
let _ = inner.insert(
"b".to_string(),
Value::Mapping({
let mut m = Mapping::new();
let _ = m.insert("c".to_string(), Value::from(2));
m
}),
);
let seq = Value::Sequence(vec![Value::Mapping(inner)]);
let yaml = to_string(&seq).unwrap();
assert!(yaml.contains("a:"));
assert!(yaml.contains("b:"));
}
#[test]
fn ser_literal_block_non_string_fallback() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_lit_str"),
Value::from(42),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_folded_block_non_string_fallback() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_fold_str"),
Value::from(42),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_commented_wrong_length() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_commented"),
Value::Sequence(vec![Value::from(1)]),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("1"));
}
#[test]
fn ser_commented_non_sequence() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_commented"),
Value::from(42),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_unknown_internal_tag() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_unknown"),
Value::from(42),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_multi_doc() {
let docs = vec![1, 2, 3];
let yaml = to_string_multi(&docs).unwrap();
assert!(yaml.contains("---"));
assert!(yaml.contains("1"));
assert!(yaml.contains("2"));
assert!(yaml.contains("3"));
}
#[test]
fn ser_literal_block_via_lit_str() {
let v = LitStr("hello\nworld\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("|"));
}
#[test]
fn ser_folded_block_via_fold_str() {
let v = FoldStr("hello\nworld\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains(">"));
}
#[test]
fn ser_literal_block_keep_chomping() {
let v = LitStr("hello\nworld\n\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("|+"));
}
#[test]
fn ser_literal_block_strip_chomping() {
let v = LitStr("hello\nworld");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("|-"));
}
#[test]
fn ser_folded_block_keep_chomping() {
let v = FoldStr("hello\nworld\n\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains(">+"));
}
#[test]
fn ser_folded_block_strip_chomping() {
let v = FoldStr("hello\nworld");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains(">-"));
}
#[test]
fn singleton_map_recursive_with_tagged() {
use noyalib::with::singleton_map_recursive;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Action {
Click { x: i32, y: i32 },
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
#[serde(with = "singleton_map_recursive")]
action: Action,
}
let config = Config {
action: Action::Click { x: 1, y: 2 },
};
let yaml = to_string(&config).unwrap();
let roundtrip: Config = from_str(&yaml).unwrap();
assert_eq!(config, roundtrip);
}
#[test]
fn singleton_map_with_serialize_non_mapping() {
use noyalib::with::singleton_map_with;
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("test"),
Value::from("inner"),
)));
let result = singleton_map_with::deserialize_with(&tagged, |s| s.to_uppercase());
let _: Result<Value> = result;
}
#[test]
fn scanner_single_quoted_break_followed_by_break() {
let yaml = "'a\n\nb'\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("a"));
assert!(s.contains("b"));
}
#[test]
fn scanner_double_quoted_break_followed_by_normal() {
let yaml = "\"hello\nworld\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello world"));
}
#[test]
fn scanner_block_scalar_empty_only_lines() {
let yaml = "|\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains('\n') || s.is_empty());
}
#[test]
fn loader_quoted_scalar_with_tag() {
let yaml = "!!str 'hello'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn loader_tag_with_generic_handle() {
let yaml = "!mytag value\n";
let v: Value = from_str(yaml).unwrap();
if let Value::Tagged(t) = &v {
assert_eq!(t.tag().as_str(), "!mytag");
}
}
#[test]
fn scanner_single_quoted_crlf() {
let yaml = "'hello\r\nworld'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello world"));
}
#[test]
fn scanner_double_quoted_crlf() {
let yaml = "\"hello\r\nworld\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello world"));
}
#[test]
fn scanner_skip_line_crlf() {
let yaml = "a: 1\r\nb: 2\r\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn scanner_block_scalar_with_comment() {
let yaml = "| # this is a comment\n text\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("text\n"));
}
#[test]
fn events_document_end_marker() {
let yaml = "---\na: 1\n...\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn events_indentless_sequence_empty_entries() {
let yaml = "key:\n - a\n -\n - b\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"][0].as_str(), Some("a"));
assert!(v["key"][1].is_null());
assert_eq!(v["key"][2].as_str(), Some("b"));
}
#[test]
fn events_flow_sequence_entry_mapping_end() {
let yaml = "[a: 1, b: 2]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0]["a"].as_i64(), Some(1));
}
#[test]
fn loader_merge_key_value_push() {
let yaml = "base: &base\n x: 1\n y: 2\nchild:\n <<: *base\n z: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["child"]["x"].as_i64(), Some(1));
assert_eq!(v["child"]["y"].as_i64(), Some(2));
assert_eq!(v["child"]["z"].as_i64(), Some(3));
}
#[test]
fn loader_duplicate_key_last_replaces() {
let yaml = "a: 1\nb: 2\na: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(3));
}
#[test]
fn ser_sequence_nested_mapping_and_sequence() {
let mut m = Mapping::new();
let _ = m.insert(
"x".to_string(),
Value::Sequence(vec![Value::from(1), Value::from(2)]),
);
let _ = m.insert("y".to_string(), Value::from(3));
let seq = Value::Sequence(vec![Value::Mapping(m.clone()), Value::Mapping(m)]);
let yaml = to_string(&seq).unwrap();
assert!(yaml.contains("x:"));
assert!(yaml.contains("y:"));
}
#[test]
fn ser_sequence_with_nested_sequence() {
let seq = Value::Sequence(vec![Value::Sequence(vec![Value::from(1), Value::from(2)])]);
let yaml = to_string(&seq).unwrap();
assert!(yaml.contains("- 1"));
}
#[test]
fn scanner_directive() {
let yaml = "%YAML 1.2\n---\na: 1\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
}
#[test]
fn scanner_single_quoted_leading_break_then_spaces() {
let yaml = "'hello\n world'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello world"));
}
#[test]
fn scanner_double_quoted_u_escape() {
let yaml = "\"\\u0042\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("B"));
}
#[test]
fn scanner_double_quoted_big_u_escape() {
let yaml = "\"\\U00000043\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("C"));
}
#[test]
fn scanner_double_quoted_various_escapes() {
let yaml = "\"\\0\\a\\b\\t\\n\\v\\f\\r\\e\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains('\0'));
assert!(s.contains('\x07'));
assert!(s.contains('\x08'));
assert!(s.contains('\t'));
assert!(s.contains('\n'));
}
#[test]
fn scanner_double_quoted_special_escapes() {
let yaml = "\"\\N\\_\\L\\P\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains('\u{0085}'));
assert!(s.contains('\u{00A0}'));
assert!(s.contains('\u{2028}'));
assert!(s.contains('\u{2029}'));
}
#[test]
fn scanner_block_scalar_more_indented_literal() {
let yaml = "|\n normal\n more indented\n normal again\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("normal"));
assert!(s.contains(" more indented"));
}
#[test]
fn loader_tagged_null() {
let yaml = "!!null ~\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_null());
}
#[test]
fn loader_tagged_str() {
let yaml = "!!str 42\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("42"));
}
#[test]
fn loader_tagged_float_inf() {
let yaml = "!!float .inf\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_f64(), Some(f64::INFINITY));
}
#[test]
fn loader_tagged_float_nan() {
let yaml = "!!float .nan\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_f64().unwrap().is_nan());
}
#[test]
fn scanner_block_scalar_folded_leading_blank_line() {
let yaml = ">\n normal line\n leading space\n another normal\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("normal line"));
}
#[test]
fn scanner_dash_comma_in_flow() {
let yaml = "[a, b, c]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_str(), Some("a"));
assert_eq!(v[2].as_str(), Some("c"));
}
#[test]
fn scanner_question_mark_in_flow() {
let yaml = "[?]\n";
let result: Result<Value> = from_str(yaml);
let _ = result; }
#[test]
fn events_block_mapping_value_after_value() {
let yaml = "? a\n:\n? b\n: val\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_str(), Some("val"));
}
#[test]
fn scanner_comment_skipping() {
let yaml = "a: 1 # comment\nb: 2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn scanner_roll_indent_returns_in_flow() {
let yaml = "{a: [1, 2]}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"][0].as_i64(), Some(1));
}
#[test]
fn events_flow_mapping_empty_value_path() {
let yaml = "{a, b: 1}\n";
let result: Result<Value> = from_str(yaml);
let _ = result; }
#[test]
fn loader_various_key_types() {
let yaml = "true: yes_val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["true"].as_str(), Some("yes_val"));
let yaml = "null: null_val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["null"].as_str(), Some("null_val"));
let yaml = "1.5: float_val\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["1.5"].as_str(), Some("float_val"));
}
#[test]
fn scanner_double_quoted_line_escape_cr_lf() {
let yaml = "\"hello\\\r\nworld\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("helloworld"));
}
#[test]
fn loader_tagged_bool_invalid() {
let yaml = "!!bool notbool\n";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_tagged_int_invalid() {
let yaml = "!!int notint\n";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_tagged_float_invalid() {
let yaml = "!!float notfloat\n";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_single_quoted_multi_crlf_breaks() {
let yaml = "'a\r\n\r\nb'\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("a"));
assert!(s.contains("b"));
}
#[test]
fn scanner_double_quoted_multi_crlf_breaks() {
let yaml = "\"a\r\n\r\nb\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("a"));
assert!(s.contains("b"));
}
#[test]
fn scanner_plain_scalar_with_crlf_breaks() {
let yaml = "key: val\r\nother: thing\r\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("val"));
assert_eq!(v["other"].as_str(), Some("thing"));
}
#[test]
fn scanner_block_scalar_keep_trailing() {
let yaml = "|+\n keep\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.ends_with("\n\n"));
}
#[test]
fn scanner_block_scalar_strip_trailing() {
let yaml = "|-\n strip\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert_eq!(s, "strip");
}
#[test]
fn scanner_block_scalar_clip_trailing() {
let yaml = "|\n clip\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.ends_with('\n'));
assert!(!s.ends_with("\n\n"));
}
#[test]
fn scanner_eof_token_stream() {
let yaml = "~\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_null());
}
#[test]
fn events_block_sequence_bad_token() {
let result: Result<Value> = from_str("- a\n }\n");
let _ = result;
}
#[test]
fn ser_string_with_control_chars() {
let yaml = to_string(&Value::from("hello\x01world")).unwrap();
assert!(yaml.contains('"'));
assert!(yaml.contains("\\x01"));
}
#[test]
fn ser_tagged_value_with_string() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!custom"),
Value::from("hello"),
)));
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("!custom"));
assert!(yaml.contains("hello"));
}
#[test]
fn scanner_block_scalar_folded_multi_breaks() {
let yaml = ">\n line1\n\n\n line2\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("line1"));
assert!(s.contains("line2"));
}
#[test]
fn loader_merge_with_scalar_value() {
let yaml = "val: &val hello\nresult:\n <<: *val\n b: 2\n";
let result: Result<Value> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn de_deserialize_identifier_via_from_value_enum() {
#[derive(Debug, Deserialize, PartialEq)]
enum Direction {
North,
South,
}
let mut m = Mapping::new();
m.insert("North", Value::Null);
let v = Value::Mapping(m);
let d: Direction = from_value(&v).unwrap();
assert_eq!(d, Direction::North);
}
#[test]
fn de_deserialize_identifier_via_from_value_struct_fields() {
#[derive(Debug, Deserialize, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let mut m = Mapping::new();
m.insert("x", Value::from(10));
m.insert("y", Value::from(20));
let v = Value::Mapping(m);
let p: Point = from_value(&v).unwrap();
assert_eq!(p, Point { x: 10, y: 20 });
}
#[test]
fn de_spanned_map_access_complete_lifecycle() {
let yaml = "test_value\n";
let spanned: Spanned<String> = from_str(yaml).unwrap();
assert_eq!(*spanned, "test_value");
assert!(spanned.start.line() < 100);
assert!(spanned.start.column() < 100);
assert!(spanned.start.index() < 100);
assert!(spanned.end.line() < 100);
assert!(spanned.end.column() < 100);
assert!(spanned.end.index() < 100);
}
#[test]
fn de_spanned_in_struct_from_str() {
#[derive(Debug, Deserialize)]
struct Config {
host: Spanned<String>,
port: Spanned<u16>,
debug: Spanned<bool>,
}
let yaml = "host: localhost\nport: 8080\ndebug: true\n";
let config: Config = from_str(yaml).unwrap();
assert_eq!(*config.host, "localhost");
assert_eq!(*config.port, 8080);
assert!(*config.debug);
}
#[test]
fn events_indentless_sequence_at_mapping_level() {
let yaml = "items:\n - a\n - b\n - c\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["items"][0].as_str(), Some("a"));
assert_eq!(v["items"][1].as_str(), Some("b"));
assert_eq!(v["items"][2].as_str(), Some("c"));
}
#[test]
fn events_document_content_state() {
let yaml = "---\nhello\n...\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn events_document_content_empty_before_end() {
let yaml = "---\n...\n---\nhello\n";
let docs: Vec<Value> = load_all(yaml).unwrap().filter_map(|r| r.ok()).collect();
assert!(docs[0].is_null());
assert_eq!(docs[1].as_str(), Some("hello"));
}
#[test]
fn events_anchor_after_tag_parsed() {
let yaml = "!!str &myanchor hello\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn events_flow_sequence_start_in_node() {
let yaml = "data: [1, 2, 3]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["data"][0].as_i64(), Some(1));
}
#[test]
fn events_flow_mapping_start_in_node() {
let yaml = "data: {x: 1, y: 2}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["data"]["x"].as_i64(), Some(1));
}
#[test]
fn events_tag_only_empty_scalar() {
let yaml = "!!str :\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn events_indentless_sequence_empty_entry_followed_by_key() {
let yaml = "a:\n - \n - x\nb: 2\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"][0].is_null());
assert_eq!(v["a"][1].as_str(), Some("x"));
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn events_block_mapping_key_followed_by_key() {
let yaml = "? a\n? b\n: value\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["a"].is_null());
assert_eq!(v["b"].as_str(), Some("value"));
}
#[test]
fn events_flow_sequence_entry_mapping_key_empty() {
let yaml = "[? : val]\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn events_flow_mapping_explicit_key() {
let yaml = "{? key : val, other: 2}\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn events_flow_mapping_explicit_key_empty_value_after_key() {
let yaml = "{? , a: 1}\n";
let result: Result<Value> = from_str(yaml);
let _ = result; }
#[test]
fn loader_scalar_event_with_quoted_style_and_tag() {
let yaml = "!!str 'hello'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn loader_max_depth_exceeded() {
let config = ParserConfig::new().max_depth(3);
let yaml = "a:\n b:\n c:\n d: 1\n";
let result: Result<Value> = from_str_with_config(yaml, &config);
assert!(result.is_err());
}
#[test]
fn loader_sequence_end_with_anchor() {
let yaml = "a: &myseq\n - 1\n - 2\nb: *myseq\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["b"][0].as_i64(), Some(1));
}
#[test]
fn loader_mapping_end_with_anchor() {
let yaml = "template: &tmpl\n host: localhost\n port: 80\nsite: *tmpl\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["site"]["host"].as_str(), Some("localhost"));
}
#[test]
fn loader_mapping_value_frame_merge() {
let yaml = "base: &base\n a: 1\nderived:\n <<: *base\n b: 2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["derived"]["a"].as_i64(), Some(1));
assert_eq!(v["derived"]["b"].as_i64(), Some(2));
}
#[test]
fn loader_mapping_value_frame_first_policy() {
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let yaml = "a: 1\nb: 2\na: 3\n";
let v: Value = from_str_with_config(yaml, &config).unwrap();
assert_eq!(v["a"].as_i64(), Some(1)); assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn loader_non_scalar_key_error() {
let yaml = "[a, b]: value\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn loader_tag_with_custom_handle() {
let yaml = "!custom_tag hello\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_tagged() || v.as_str().is_some());
}
#[test]
fn loader_alias_large_string() {
let long_str = "x".repeat(100);
let yaml = format!("big: &big {}\nref1: *big\nref2: *big\n", long_str);
let v: Value = from_str(&yaml).unwrap();
assert_eq!(v["ref1"].as_str().unwrap().len(), 100);
assert_eq!(v["ref2"].as_str().unwrap().len(), 100);
}
#[test]
fn scanner_eof_mid_structure() {
let result: Result<Value> = from_str("[");
assert!(result.is_err());
}
#[test]
fn scanner_simple_key_tracking() {
let yaml = "{a: 1, b: 2, c: 3}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["c"].as_i64(), Some(3));
}
#[test]
fn scanner_colon_followed_by_colon() {
let yaml = "a:: b\n";
let result: Result<Value> = from_str(yaml);
let _ = result; }
#[test]
fn scanner_block_entry_not_allowed() {
let yaml = "a: 1\n- b\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn scanner_mapping_key_not_allowed() {
let yaml = "a: 1\n ? b\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn scanner_value_after_simple_key_multiline() {
let yaml = "key: value\nnested:\n inner_key: inner_value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["nested"]["inner_key"].as_str(), Some("inner_value"));
}
#[test]
fn scanner_value_not_allowed_context() {
let yaml = ": standalone_value\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn scanner_plain_scalar_stops_at_colon_space() {
let yaml = "key: value with spaces\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value with spaces"));
}
#[test]
fn scanner_plain_scalar_stops_at_flow_indicators() {
let yaml = "[hello world, second]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0].as_str(), Some("hello world"));
assert_eq!(v[1].as_str(), Some("second"));
}
#[test]
fn scanner_plain_scalar_multiline_continuation() {
let yaml = "key: line1\n line2\n line3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("line1 line2 line3"));
}
#[test]
fn scanner_single_quoted_trailing_whitespace() {
let yaml = "'hello \n world'\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("hello"));
assert!(s.contains("world"));
}
#[test]
fn scanner_double_quoted_escape_after_line_break() {
let yaml = "\"line1\n\\tline2\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.contains("line1"));
assert!(s.contains('\t'));
}
#[test]
fn ser_looks_like_number_dot_digit_pattern() {
let v = Value::from(".5");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('\'') || yaml.contains('"'));
let v = Value::from(".123");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('\'') || yaml.contains('"'));
}
#[test]
fn ser_reserved_words_comprehensive() {
for word in &[
"true", "false", "null", "~", "True", "False", "Null", "TRUE", "FALSE", "NULL",
] {
let v = Value::from(*word);
let yaml = to_string(&v).unwrap();
assert!(
yaml.contains('\'') || yaml.contains('"'),
"Expected '{}' to be quoted, got: {}",
word,
yaml
);
}
}
#[test]
fn ser_first_char_single_quote_needs_double() {
let v = Value::from("'quoted");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('"'));
}
#[test]
fn ser_literal_block_with_leading_space() {
let v = LitStr(" indented first line\nsecond line\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('|'));
}
#[test]
fn ser_folded_block_with_leading_space() {
let v = FoldStr(" indented first line\nsecond line\n");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('>'));
}
#[test]
fn spanned_deserialize_calls_deserialize_struct() {
let yaml = "42\n";
let v: Spanned<i64> = from_str(yaml).unwrap();
assert_eq!(*v, 42);
let _ = v.start;
let _ = v.end;
}
#[test]
fn spanned_expecting_message() {
let result: std::result::Result<Spanned<String>, _> = serde_json::from_str("42");
let _ = result;
}
#[test]
fn spanned_unknown_field_skipped() {
let v = Value::from("hello");
let result: Spanned<String> = from_value(&v).unwrap();
assert_eq!(*result, "hello");
}
#[test]
fn value_map_access_next_value_via_ref_value() {
#[derive(Debug, Deserialize, PartialEq)]
struct Pair {
key: String,
val: i64,
}
let mut m = Mapping::new();
m.insert("key", Value::from("hello"));
m.insert("val", Value::from(42));
let v = Value::Mapping(m);
let p: Pair = Deserialize::deserialize(&v).unwrap();
assert_eq!(p.key, "hello");
assert_eq!(p.val, 42);
}
#[test]
fn ref_value_deserialize_enum_mapping() {
#[derive(Debug, Deserialize, PartialEq)]
enum Animal {
Dog { name: String },
}
let mut inner = Mapping::new();
inner.insert("name", Value::from("Rex"));
let mut m = Mapping::new();
m.insert("Dog", Value::Mapping(inner));
let v = Value::Mapping(m);
let a: Animal = from_value(&v).unwrap();
assert_eq!(
a,
Animal::Dog {
name: "Rex".to_string()
}
);
}
#[test]
fn ref_value_deserialize_struct_via_mapping() {
#[derive(Debug, Deserialize, PartialEq)]
struct Rgb {
r: u8,
g: u8,
b: u8,
}
let mut m = Mapping::new();
m.insert("r", Value::from(255));
m.insert("g", Value::from(128));
m.insert("b", Value::from(0));
let v = Value::Mapping(m);
let c: Rgb = Deserialize::deserialize(&v).unwrap();
assert_eq!(
c,
Rgb {
r: 255,
g: 128,
b: 0
}
);
}
#[test]
fn ref_value_deserialize_struct_spanned_via_value() {
let v = Value::from("spanned_value");
let result: Spanned<String> = Deserialize::deserialize(&v).unwrap();
assert_eq!(*result, "spanned_value");
}
#[test]
fn ref_value_deserialize_struct_spanned_nested_in_mapping() {
#[derive(Debug, Deserialize)]
struct Item {
name: Spanned<String>,
count: Spanned<i32>,
}
let mut m = Mapping::new();
m.insert("name", Value::from("widget"));
m.insert("count", Value::from(5));
let v = Value::Mapping(m);
let item: Item = Deserialize::deserialize(&v).unwrap();
assert_eq!(*item.name, "widget");
assert_eq!(*item.count, 5);
}
#[test]
fn singleton_map_recursive_transform_tagged() {
use noyalib::with::singleton_map_recursive;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Op {
Add(i32),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Ops {
#[serde(with = "singleton_map_recursive")]
ops: Vec<Op>,
}
let ops = Ops {
ops: vec![Op::Add(1), Op::Add(2)],
};
let yaml = to_string(&ops).unwrap();
let roundtrip: Ops = from_str(&yaml).unwrap();
assert_eq!(ops, roundtrip);
}
#[test]
fn singleton_map_with_serialize_fallback_non_mapping_non_string() {
use noyalib::with::singleton_map_with;
let value = 42i32;
let result: Value =
singleton_map_with::serialize_with(&value, Serializer, |s| s.to_uppercase()).unwrap();
assert_eq!(result.as_i64(), Some(42));
}
#[test]
fn singleton_map_with_serialize_fallback_sequence() {
use noyalib::with::singleton_map_with;
let value = vec![1, 2, 3];
let result: Value =
singleton_map_with::serialize_with(&value, Serializer, |s| s.to_uppercase()).unwrap();
assert!(result.is_sequence());
}
#[test]
fn singleton_map_with_deserialize_tagged_transform() {
use noyalib::with::singleton_map_with;
let inner_map = {
let mut m = Mapping::new();
m.insert("key", Value::from("val"));
m
};
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!mytag"),
Value::Mapping(inner_map),
)));
let result: Result<Value> = singleton_map_with::deserialize_with(&tagged, |s| s.to_uppercase());
let _ = result;
}
#[test]
fn events_flow_mapping_empty_value_state() {
let yaml = "{a, b: 1}\n";
let result: Result<Value> = from_str(yaml);
if let Ok(v) = result {
assert!(v["a"].is_null());
}
}
#[test]
fn events_flow_sequence_explicit_key_mapping() {
let yaml = "[? a: b]\n";
let result: Result<Value> = from_str(yaml);
let _ = result;
}
#[test]
fn spanned_mapping_value_from_str() {
#[derive(Debug, Deserialize)]
struct Doc {
data: Spanned<HashMap<String, i32>>,
}
let yaml = "data:\n x: 1\n y: 2\n";
let doc: Doc = from_str(yaml).unwrap();
assert_eq!(*doc.data.get("x").unwrap(), 1);
assert_eq!(*doc.data.get("y").unwrap(), 2);
}
#[test]
fn events_flow_sequence_not_first_entry() {
let yaml = "[1, 2, 3, 4, 5]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[4].as_i64(), Some(5));
}
#[test]
fn events_flow_mapping_key_not_first() {
let yaml = "{a: 1, b: 2, c: 3}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["c"].as_i64(), Some(3));
}
#[test]
fn loader_span_tree_sequence_and_mapping() {
let yaml = "list:\n - name: a\n val: 1\n - name: b\n val: 2\nother:\n - 10\n - 20\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["list"][0]["name"].as_str(), Some("a"));
assert_eq!(v["other"][1].as_i64(), Some(20));
}
#[test]
fn fmt_commented_inner_serialize_roundtrip() {
let commented = Commented::new(42i32, "the answer");
let v = to_value(&commented).unwrap();
assert!(v.is_tagged());
}
#[test]
fn events_flow_sequence_entry_mapping_value_with_content() {
let yaml = "[a: 1]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v[0]["a"].as_i64(), Some(1));
}
#[test]
fn events_flow_sequence_entry_mapping_value_empty() {
let yaml = "[a:, b]\n";
let v: Value = from_str(yaml).unwrap();
assert!(v[0]["a"].is_null());
}
#[test]
fn scanner_plain_scalar_with_comment_terminator() {
let yaml = "key: value # this is a comment\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("value"));
}
#[test]
fn loader_alias_expansion_limit() {
let config = ParserConfig::new().max_alias_expansions(1);
let yaml = "a: &a hello\nb: *a\nc: *a\n";
let result: Result<Value> = from_str_with_config(yaml, &config);
assert!(result.is_err());
}
#[test]
fn events_indentless_sequence_with_nested_nodes() {
let yaml = "mapping:\n - key: value\n - other: data\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["mapping"][0]["key"].as_str(), Some("value"));
}
#[test]
fn scanner_plain_scalar_in_flow_with_colon() {
let yaml = "{key: http://example.com}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str(), Some("http://example.com"));
}