use noyalib::{DuplicateKeyPolicy, ParserConfig, Value, from_str, from_str_with_config};
#[test]
fn yr2_70_utf8_in_flow_mapping() {
let yaml = r#"- "(":
- test:
if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak'
then: [test: {if: "$Verbosity='Terse'", then: [T: "mở ngoặc"], else: [T: "mở ngoặc đơn"]}]
else: [T: "mở ngoặc đơn"]
"#;
let result: Result<Value, _> = from_str(yaml);
assert!(
result.is_ok(),
"UTF-8 flow mapping should parse: {:?}",
result.err()
);
}
#[test]
fn yr2_70_utf8_scalar_values() {
let yaml = "name: héllo wörld\ncity: Zürich\nemoji: 🦀\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["name"].as_str(), Some("héllo wörld"));
assert_eq!(v["city"].as_str(), Some("Zürich"));
assert_eq!(v["emoji"].as_str(), Some("🦀"));
}
#[test]
fn yr2_69_flow_seq_closing_bracket_indentation() {
let yaml = "foo: [\n 'a', 'b', 'c'\n]\n";
let result: Result<Value, _> = from_str(yaml);
if let Ok(v) = &result {
let seq = v["foo"].as_sequence().unwrap();
assert_eq!(seq.len(), 3);
}
}
#[test]
fn yr2_30_multiline_flow_seq_trailing_comma() {
let yaml = "foo: [\n 'a',\n 'b',\n 'c',\n]\n";
let result: Result<Value, _> = from_str(yaml);
if let Ok(v) = &result {
let seq = v["foo"].as_sequence().unwrap();
assert_eq!(seq.len(), 3);
}
}
#[test]
fn yr2_30_flow_mapping_multiline() {
let yaml = "foo: {\n a: 1,\n b: 2\n}\n";
let result: Result<Value, _> = from_str(yaml);
if let Ok(v) = &result {
assert_eq!(v["foo"]["a"].as_i64(), Some(1));
assert_eq!(v["foo"]["b"].as_i64(), Some(2));
}
}
#[test]
fn yr2_23_duplicate_keys_default_last_wins() {
let yaml = "a: hello\na: world\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_str(), Some("world"));
}
#[test]
fn yr2_23_duplicate_keys_error_policy() {
let yaml = "a: hello\na: world\n";
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::Error);
let result: Result<Value, _> = from_str_with_config(yaml, &config);
assert!(result.is_err(), "duplicate keys should error");
}
#[test]
fn yr2_23_duplicate_keys_first_wins() {
let yaml = "a: hello\na: world\n";
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let v: Value = from_str_with_config(yaml, &config).unwrap();
assert_eq!(v["a"].as_str(), Some("hello"));
}
#[test]
fn yr2_25_folded_scalar_clip_chomping() {
let yaml = ">\n This is\n multiline\n string\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert_eq!(s, "This is multiline string\n");
}
#[test]
fn yr2_25_folded_scalar_strip_chomping() {
let yaml = ">-\n This is\n multiline\n string\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert_eq!(s, "This is multiline string");
}
#[test]
fn yr2_25_folded_scalar_keep_chomping() {
let yaml = ">+\n This is\n multiline\n string\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.as_str().unwrap();
assert!(s.ends_with("string\n\n"));
}
#[test]
fn yr2_11_tag_with_numeric_suffix() {
let yaml = "--- !custom 42\n";
let result: Result<Value, _> = from_str(yaml);
assert!(
result.is_ok(),
"tag with value should parse: {:?}",
result.err()
);
}
#[test]
fn yr2_11_tag_directive_and_custom_tag() {
let yaml = "--- !mytag value\n";
let v: Value = from_str(yaml).unwrap();
match &v {
Value::Tagged(t) => {
assert!(t.tag().as_str().contains("mytag"));
}
Value::String(s) => {
assert_eq!(s, "value");
}
_ => {}
}
}
#[test]
fn yr2_29_deep_indentation_block_scalar() {
let yaml = "\
a:
b:
c:
d:
e:
f:
g:
h:
i: |
deeply indented block scalar
";
let v: Value = from_str(yaml).unwrap();
let inner = v.get_path("a.b.c.d.e.f.g.h.i").and_then(|v| v.as_str());
assert_eq!(inner, Some("deeply indented block scalar\n"));
}
#[test]
fn yr2_29_comment_between_directive_and_doc_start() {
let yaml = "%YAML 1.2\n# This is a comment\n---\nfoo: bar\n";
let result: Result<Value, _> = from_str(yaml);
let _ = result;
}
#[test]
fn yr2_21_comments_stripped_data_preserved() {
let yaml = "value:\n # comment here\n '002':\n - name: foo\n";
let v: Value = from_str(yaml).unwrap();
let name = v
.get_path("value.002")
.and_then(|v| v.as_sequence())
.and_then(|s| s.first())
.and_then(|v| v.get("name"))
.and_then(|v| v.as_str());
assert_eq!(name, Some("foo"));
}
#[test]
fn yr2_21_inline_comments_stripped() {
let yaml = "host: localhost # primary host\nport: 5432 # postgres default\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["host"].as_str(), Some("localhost"));
assert_eq!(v["port"].as_i64(), Some(5432));
}
#[test]
fn yr2_27_source_spans_via_spanned() {
use noyalib::Spanned;
use serde::Deserialize;
#[derive(Deserialize)]
struct Config {
host: Spanned<String>,
port: Spanned<u16>,
}
let yaml = "host: localhost\nport: 8080\n";
let config: Config = from_str(yaml).unwrap();
assert_eq!(config.host.value, "localhost");
assert!(config.host.start.line() >= 1);
assert_eq!(config.port.value, 8080);
assert!(config.port.start.line() >= 1);
}
#[test]
fn yr2_34_basic_tag_handles() {
let yaml = "--- !e!foo bar\n";
let result: Result<Value, _> = from_str(yaml);
let _ = result;
}
#[test]
fn yr2_34_standard_tags() {
let yaml = "!!int 42\n";
let v: Value = from_str(yaml).unwrap();
match &v {
Value::Number(_) | Value::Tagged(_) => {} other => panic!("expected number or tagged, got {:?}", other),
}
}
#[test]
fn yr2_22_multi_document_parsing() {
let yaml = "---\nfoo: bar\n---\nbaz: qux\n";
let docs = noyalib::load_all(yaml).unwrap();
let docs: Vec<_> = docs.collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(docs.len(), 2);
assert_eq!(docs[0]["foo"].as_str(), Some("bar"));
assert_eq!(docs[1]["baz"].as_str(), Some("qux"));
}
#[test]
fn yr2_22_multi_document_typed() {
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct Doc {
name: String,
}
let yaml = "---\nname: first\n---\nname: second\n";
let docs: Vec<Doc> = noyalib::load_all_as(yaml).unwrap();
assert_eq!(docs.len(), 2);
assert_eq!(docs[0].name, "first");
assert_eq!(docs[1].name, "second");
}
#[test]
fn mixed_utf8_in_keys_and_values() {
let yaml = "日本語: こんにちは\nclé: valeur\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["日本語"].as_str(), Some("こんにちは"));
assert_eq!(v["clé"].as_str(), Some("valeur"));
}
#[test]
fn deeply_nested_flow_with_utf8() {
let yaml = "{a: {b: {c: \"café\"}}}\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"]["b"]["c"].as_str(), Some("café"));
}
#[test]
fn empty_flow_collections() {
let yaml = "empty_seq: []\nempty_map: {}\n";
let v: Value = from_str(yaml).unwrap();
assert!(v["empty_seq"].as_sequence().unwrap().is_empty());
assert!(v["empty_map"].as_mapping().unwrap().is_empty());
}
#[test]
fn anchor_alias_in_flow() {
let yaml = "[&a 1, *a, *a]\n";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq[0].as_i64(), Some(1));
assert_eq!(seq[1].as_i64(), Some(1));
assert_eq!(seq[2].as_i64(), Some(1));
}
#[test]
fn literal_block_scalar_with_explicit_indent() {
let yaml = "script: |2\n #!/bin/bash\n echo hello\n";
let result: Result<Value, _> = from_str(yaml);
if let Ok(v) = result {
let s = v["script"].as_str().unwrap();
assert!(s.contains("#!/bin/bash"));
}
}