1#![deny(unsafe_code)]
12#![warn(missing_docs)]
13#![warn(clippy::all)]
14#![warn(clippy::pedantic)]
15#![allow(clippy::module_name_repetitions)]
16#![allow(clippy::result_large_err)]
17#![allow(clippy::uninlined_format_args)]
18#![allow(clippy::approx_constant)]
19#![allow(clippy::too_many_lines)]
20#![allow(clippy::unnecessary_wraps)]
21#![allow(clippy::missing_errors_doc)]
22#![allow(clippy::must_use_candidate)]
23#![allow(clippy::return_self_not_must_use)]
24#![allow(clippy::unused_self)]
25#![allow(clippy::only_used_in_recursion)]
26#![allow(clippy::manual_let_else)]
27#![allow(clippy::needless_pass_by_value)]
28#![allow(clippy::map_unwrap_or)]
29#![allow(clippy::redundant_closure_for_method_calls)]
30#![allow(clippy::inefficient_to_string)]
31#![allow(clippy::doc_markdown)]
32#![allow(clippy::match_same_arms)]
33#![allow(clippy::unnecessary_map_or)]
34#![allow(clippy::len_zero)]
35#![allow(clippy::field_reassign_with_default)]
36#![allow(clippy::single_match_else)]
37#![allow(clippy::clone_on_copy)]
38#![allow(clippy::unwrap_or_default)]
39#![allow(clippy::cast_possible_truncation)]
40#![allow(clippy::format_push_string)]
41#![allow(clippy::missing_panics_doc)]
42#![allow(clippy::struct_excessive_bools)]
43#![allow(clippy::cast_sign_loss)]
44#![allow(clippy::explicit_iter_loop)]
45#![allow(clippy::ignored_unit_patterns)]
46#![allow(clippy::no_effect_underscore_binding)]
47#![allow(clippy::collapsible_if)]
48#![allow(clippy::comparison_chain)]
49#![allow(clippy::collapsible_else_if)]
50#![allow(clippy::redundant_pattern_matching)]
51#![allow(clippy::cast_precision_loss)]
52#![allow(dead_code)]
53#![allow(clippy::needless_pass_by_ref_mut)]
54#![allow(clippy::missing_const_for_fn)]
55#![allow(clippy::manual_contains)]
56#![allow(clippy::option_if_let_else)]
57#![allow(clippy::elidable_lifetime_names)]
58#![allow(clippy::derive_partial_eq_without_eq)]
59#![allow(clippy::needless_borrow)]
60#![allow(clippy::cast_possible_wrap)]
61#![allow(clippy::wildcard_imports)]
62#![allow(clippy::or_fun_call)]
63#![allow(clippy::if_not_else)]
64#![allow(clippy::manual_strip)]
65#![allow(clippy::range_plus_one)]
66#![allow(clippy::get_first)]
67#![allow(clippy::use_self)]
68#![allow(clippy::needless_raw_string_hashes)]
69#![allow(unused_mut)]
70#![allow(clippy::single_match)]
71#![allow(clippy::manual_flatten)]
72#![allow(unused_variables)]
73#![allow(clippy::while_let_on_iterator)]
74
75pub mod composer;
76pub mod composer_borrowed;
77pub mod composer_comments;
78pub mod composer_optimized;
79pub mod constructor;
80pub mod emitter;
81pub mod error;
82pub mod limits;
83pub mod parser;
84pub mod position;
85pub mod profiling;
86pub mod representer;
87pub mod resolver;
88pub mod scanner;
89pub mod schema;
90pub mod serializer;
91#[cfg(feature = "async")]
92pub mod streaming_async;
93pub mod streaming_enhanced;
94pub mod tag;
95pub mod value;
96pub mod value_borrowed;
97pub mod version;
98pub mod yaml;
99pub mod zero_copy_value;
100pub mod zerocopy;
101
102pub use error::{Error, Result};
104pub use limits::{Limits, ResourceStats, ResourceTracker};
105pub use position::Position;
106pub use scanner::QuoteStyle;
107pub use schema::{
108 Schema, SchemaRule, SchemaValidator, ValidationError, ValidationResult, ValueType,
109};
110pub use value::{CommentedValue, Comments, IndentStyle, Style, Value};
111pub use value_borrowed::BorrowedValue;
112pub use yaml::{IndentConfig, LoaderType, Yaml, YamlConfig};
113pub use zero_copy_value::OptimizedValue;
114
115pub use composer::{BasicComposer, Composer};
117pub use composer_borrowed::{BorrowedComposer, ZeroCopyComposer};
118pub use composer_comments::CommentPreservingComposer;
119pub use composer_optimized::{OptimizedComposer, ReducedAllocComposer};
120pub use constructor::{
121 CommentPreservingConstructor, Constructor, RoundTripConstructor, SafeConstructor,
122};
123pub use emitter::{BasicEmitter, Emitter};
124pub use parser::{
125 BasicParser, Event, EventType, Parser, StreamingConfig, StreamingParser, StreamingStats,
126};
127pub use representer::{Representer, SafeRepresenter};
128pub use resolver::{BasicResolver, PlainScalarType, Resolver, resolve_plain_scalar};
129pub use scanner::{BasicScanner, Scanner, Token, TokenType};
130pub use serializer::{BasicSerializer, Serializer};
131pub use streaming_enhanced::{
132 StreamConfig, StreamingYamlParser, stream_from_file, stream_from_string,
133};
134pub use version::YamlVersion;
135pub use zerocopy::{ScannerStats, TokenPool, ZeroScanner, ZeroString, ZeroToken, ZeroTokenType};
136#[cfg(feature = "serde")]
139pub mod serde_integration;
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::parser::ScalarStyle;
145
146 #[test]
147 fn test_basic_functionality() {
148 let yaml = Yaml::new();
149 let value = yaml.load_str("42").unwrap();
150 assert_eq!(value, Value::Int(42));
151 }
152
153 #[test]
154 fn test_error_creation() {
155 let pos = Position::new();
156 let error = Error::parse(pos, "test error");
157 assert!(error.to_string().contains("test error"));
158 }
159
160 #[test]
161 fn test_value_types() {
162 assert_eq!(Value::Null, Value::Null);
163 assert_eq!(Value::Bool(true), Value::Bool(true));
164 assert_eq!(Value::Int(42), Value::Int(42));
165 assert_eq!(Value::Float(3.14), Value::Float(3.14));
166 assert_eq!(
167 Value::String("test".to_string()),
168 Value::String("test".to_string())
169 );
170 }
171
172 #[test]
173 fn test_anchor_alias_parsing() {
174 let yaml_with_anchors = r"
175base: &base
176 name: test
177 value: 42
178
179prod: *base
180";
181
182 let mut parser = BasicParser::new_eager(yaml_with_anchors.to_string());
183
184 let mut events = Vec::new();
185 while parser.check_event() {
186 if let Ok(Some(event)) = parser.get_event() {
187 events.push(event);
188 } else {
189 break;
190 }
191 }
192
193 let base_mapping = events.iter().find(|e| {
195 if let EventType::MappingStart { anchor, .. } = &e.event_type {
196 anchor.as_ref().map_or(false, |a| a == "base")
197 } else {
198 false
199 }
200 });
201
202 assert!(
203 base_mapping.is_some(),
204 "Should find mapping with 'base' anchor"
205 );
206
207 let alias_event = events
209 .iter()
210 .find(|e| matches!(e.event_type, EventType::Alias { .. }));
211
212 assert!(alias_event.is_some(), "Should find alias event");
213
214 if let EventType::Alias { anchor } = &alias_event.unwrap().event_type {
215 assert_eq!(anchor, "base", "Alias should reference 'base'");
216 }
217 }
218
219 #[test]
220 fn test_literal_block_scalar() {
221 let yaml_literal = r"literal: |
222 This text contains
223 multiple lines
224 with preserved newlines
225";
226
227 let mut parser = BasicParser::new_eager(yaml_literal.to_string());
228
229 let mut events = Vec::new();
230 while parser.check_event() {
231 if let Ok(Some(event)) = parser.get_event() {
232 events.push(event);
233 } else {
234 break;
235 }
236 }
237
238 let literal_scalar = events.iter().find(|e| {
240 if let EventType::Scalar { value, style, .. } = &e.event_type {
241 *style == ScalarStyle::Literal && value.contains("This text contains")
242 } else {
243 false
244 }
245 });
246
247 assert!(literal_scalar.is_some(), "Should find literal scalar");
248
249 if let EventType::Scalar { value, .. } = &literal_scalar.unwrap().event_type {
250 assert!(
251 value.contains('\n'),
252 "Literal scalar should preserve newlines"
253 );
254 assert!(
255 value.contains("This text contains"),
256 "Should contain the literal text"
257 );
258 }
259 }
260
261 #[test]
262 fn test_folded_block_scalar() {
263 let yaml_folded = r"folded: >
264 This text will be
265 folded into a
266 single line
267";
268
269 let mut parser = BasicParser::new_eager(yaml_folded.to_string());
270
271 let mut events = Vec::new();
272 while parser.check_event() {
273 if let Ok(Some(event)) = parser.get_event() {
274 events.push(event);
275 } else {
276 break;
277 }
278 }
279
280 let folded_scalar = events.iter().find(|e| {
282 if let EventType::Scalar { value, style, .. } = &e.event_type {
283 *style == ScalarStyle::Folded && value.contains("This text will be")
284 } else {
285 false
286 }
287 });
288
289 assert!(folded_scalar.is_some(), "Should find folded scalar");
290
291 if let EventType::Scalar { value, .. } = &folded_scalar.unwrap().event_type {
292 assert!(
296 value.contains("This text will be folded into a single line"),
297 "Should fold the text"
298 );
299 assert!(
300 value.ends_with('\n'),
301 "Clip-mode folded scalar keeps one trailing newline"
302 );
303 assert_eq!(
304 value.matches('\n').count(),
305 1,
306 "Internal line breaks must be folded, only one trailing newline allowed"
307 );
308 }
309 }
310
311 fn parse_scalar_value(yaml: &str) -> String {
312 let mut parser = BasicParser::new_eager(yaml.to_string());
313 let mut events = Vec::new();
314 while parser.check_event() {
315 if let Ok(Some(event)) = parser.get_event() {
316 events.push(event);
317 } else {
318 break;
319 }
320 }
321 for ev in &events {
322 if let EventType::Scalar { value, style, .. } = &ev.event_type {
323 if matches!(style, ScalarStyle::Literal | ScalarStyle::Folded) {
324 return value.clone();
325 }
326 }
327 }
328 panic!("No block scalar found in events");
329 }
330
331 #[test]
332 fn test_literal_clip_default_keeps_single_trailing_newline() {
333 let yaml = "|\n ab\n";
334 assert_eq!(parse_scalar_value(yaml), "ab\n");
335 }
336
337 #[test]
338 fn test_literal_strip_removes_trailing_newlines() {
339 let yaml = "|-\n ab\n";
340 assert_eq!(parse_scalar_value(yaml), "ab");
341 }
342
343 #[test]
344 fn test_literal_keep_preserves_trailing_blank_lines() {
345 let yaml = "|+\n ab\n\n\n";
346 assert_eq!(parse_scalar_value(yaml), "ab\n\n\n");
347 }
348
349 #[test]
350 fn test_literal_strip_removes_multiple_trailing_newlines() {
351 let yaml = "|-\n ab\n\n\n";
352 assert_eq!(parse_scalar_value(yaml), "ab");
353 }
354
355 #[test]
356 fn test_folded_clip_default_keeps_single_trailing_newline() {
357 let yaml = ">\n ab\n cd\n";
358 assert_eq!(parse_scalar_value(yaml), "ab cd\n");
359 }
360
361 #[test]
365 fn test_literal_blank_line_preserves_extra_indent_as_content() {
366 let yaml = "|+\n ab\n\n \n";
369 assert_eq!(parse_scalar_value(yaml), "ab\n\n \n");
370 }
371
372 #[test]
373 fn test_folded_strip_removes_trailing_newlines() {
374 let yaml = ">-\n ab\n";
375 assert_eq!(parse_scalar_value(yaml), "ab");
376 }
377
378 #[test]
379 fn test_folded_preserves_breaks_around_more_indented() {
380 let yaml = ">\n a b\n\n c d\n";
383 assert_eq!(parse_scalar_value(yaml), "a b\n\n c d\n");
386 }
387
388 #[test]
389 fn test_folded_collapses_breaks_between_normal_only() {
390 let yaml = ">\n a\n\n b\n";
392 assert_eq!(parse_scalar_value(yaml), "a\nb\n");
393 }
394
395 #[test]
401 fn test_same_line_scalar_in_value_position_is_value_not_key() {
402 let yaml = "? &a a\n: &b b\n: *a\n";
403 let mut parser = BasicParser::new_eager(yaml.to_string());
404 let mut events = Vec::new();
405 while parser.check_event() {
406 if let Ok(Some(event)) = parser.get_event() {
407 events.push(event);
408 } else {
409 break;
410 }
411 }
412 let entries: Vec<String> = events
413 .iter()
414 .filter_map(|e| match &e.event_type {
415 EventType::Scalar { value, anchor, .. } => {
416 Some(format!("V:{}:{}", anchor.as_deref().unwrap_or(""), value))
417 }
418 EventType::Alias { anchor } => Some(format!("A:{}", anchor)),
419 _ => None,
420 })
421 .collect();
422 assert_eq!(
423 entries,
424 vec![
425 "V:a:a".to_string(),
426 "V:b:b".to_string(),
427 "V::".to_string(),
428 "A:a".to_string(),
429 ],
430 "expected key(a&a)/val(b&b)/empty-key/alias(*a); got {entries:?}"
431 );
432 }
433
434 #[test]
438 fn test_anchor_before_implicit_key_attaches_to_key() {
439 let yaml = "&a a: b\n";
440 let mut parser = BasicParser::new_eager(yaml.to_string());
441 let mut events = Vec::new();
442 while parser.check_event() {
443 if let Ok(Some(event)) = parser.get_event() {
444 events.push(event);
445 } else {
446 break;
447 }
448 }
449 let mapping_anchor = events.iter().find_map(|e| match &e.event_type {
450 EventType::MappingStart { anchor, .. } => Some(anchor.clone()),
451 _ => None,
452 });
453 assert_eq!(
454 mapping_anchor,
455 Some(None),
456 "MappingStart should have no anchor; got {mapping_anchor:?}"
457 );
458 let first_scalar = events.iter().find_map(|e| match &e.event_type {
459 EventType::Scalar { value, anchor, .. } => Some((value.clone(), anchor.clone())),
460 _ => None,
461 });
462 assert_eq!(
463 first_scalar,
464 Some(("a".to_string(), Some("a".to_string()))),
465 "Anchor should be on the key scalar; got {first_scalar:?}"
466 );
467 }
468
469 #[test]
474 fn test_anchor_on_value_mapping_then_anchor_on_inner_key() {
475 let yaml = "top1: &node1\n &k1 key1: one\n";
476 let mut parser = BasicParser::new_eager(yaml.to_string());
477 let mut events = Vec::new();
478 while parser.check_event() {
479 match parser.get_event() {
480 Ok(Some(event)) => events.push(event),
481 Ok(None) => break,
482 Err(e) => panic!("parser error: {e:?}"),
483 }
484 }
485 let mapping_anchors: Vec<Option<String>> = events
487 .iter()
488 .filter_map(|e| match &e.event_type {
489 EventType::MappingStart { anchor, .. } => Some(anchor.clone()),
490 _ => None,
491 })
492 .collect();
493 assert_eq!(
494 mapping_anchors,
495 vec![None, Some("node1".to_string())],
496 "Expected [outer=None, inner=node1]; got {mapping_anchors:?}"
497 );
498 let key1 = events.iter().find_map(|e| match &e.event_type {
500 EventType::Scalar { value, anchor, .. } if value == "key1" => Some(anchor.clone()),
501 _ => None,
502 });
503 assert_eq!(
504 key1,
505 Some(Some("k1".to_string())),
506 "Expected &k1 on key1; got {key1:?}"
507 );
508 }
509
510 #[test]
515 fn test_flow_seq_as_implicit_key_opens_block_mapping() {
516 let yaml = "[flow]: block\n";
517 let mut parser = BasicParser::new_eager(yaml.to_string());
518 let mut events = Vec::new();
519 while parser.check_event() {
520 match parser.get_event() {
521 Ok(Some(event)) => events.push(event),
522 Ok(None) => break,
523 Err(e) => panic!("parser error: {e:?}"),
524 }
525 }
526 let event_kinds: Vec<&str> = events
527 .iter()
528 .map(|e| match &e.event_type {
529 EventType::StreamStart => "+STR",
530 EventType::StreamEnd => "-STR",
531 EventType::DocumentStart { .. } => "+DOC",
532 EventType::DocumentEnd { .. } => "-DOC",
533 EventType::MappingStart {
534 flow_style: false, ..
535 } => "+MAP",
536 EventType::MappingStart {
537 flow_style: true, ..
538 } => "+MAP{}",
539 EventType::MappingEnd => "-MAP",
540 EventType::SequenceStart {
541 flow_style: false, ..
542 } => "+SEQ",
543 EventType::SequenceStart {
544 flow_style: true, ..
545 } => "+SEQ[]",
546 EventType::SequenceEnd => "-SEQ",
547 EventType::Scalar { .. } => "=VAL",
548 EventType::Alias { .. } => "*ALIAS",
549 })
550 .collect();
551 assert_eq!(
552 event_kinds,
553 vec![
554 "+STR", "+DOC", "+MAP", "+SEQ[]", "=VAL", "-SEQ", "=VAL", "-MAP", "-DOC", "-STR"
555 ],
556 "Got: {event_kinds:?}"
557 );
558 }
559
560 #[test]
564 fn test_nested_block_sequence_spans_lines() {
565 let yaml = "- - s1_i1\n - s1_i2\n- s2\n";
566 let mut parser = BasicParser::new_eager(yaml.to_string());
567 let mut events = Vec::new();
568 while parser.check_event() {
569 match parser.get_event() {
570 Ok(Some(event)) => events.push(event),
571 Ok(None) => break,
572 Err(e) => panic!("parser error: {e:?}"),
573 }
574 }
575 let summary: Vec<String> = events
576 .iter()
577 .map(|e| match &e.event_type {
578 EventType::SequenceStart { .. } => "+SEQ".to_string(),
579 EventType::SequenceEnd => "-SEQ".to_string(),
580 EventType::Scalar { value, .. } => format!("=VAL :{value}"),
581 _ => String::new(),
582 })
583 .filter(|s| !s.is_empty())
584 .collect();
585 assert_eq!(
586 summary,
587 vec![
588 "+SEQ",
589 "+SEQ",
590 "=VAL :s1_i1",
591 "=VAL :s1_i2",
592 "-SEQ",
593 "=VAL :s2",
594 "-SEQ",
595 ],
596 "Got: {summary:?}"
597 );
598 }
599
600 #[test]
605 fn test_freestanding_anchor_attaches_to_collection() {
606 let yaml = "---\n&m\n&k a: b\n";
607 let mut parser = BasicParser::new_eager(yaml.to_string());
608 let mut events = Vec::new();
609 while parser.check_event() {
610 match parser.get_event() {
611 Ok(Some(event)) => events.push(event),
612 Ok(None) => break,
613 Err(e) => panic!("parser error: {e:?}"),
614 }
615 }
616 let map_anchor = events.iter().find_map(|e| match &e.event_type {
617 EventType::MappingStart { anchor, .. } => Some(anchor.clone()),
618 _ => None,
619 });
620 assert_eq!(
621 map_anchor,
622 Some(Some("m".to_string())),
623 "expected outer map anchor=Some(m); got {map_anchor:?}"
624 );
625 let key_a = events.iter().find_map(|e| match &e.event_type {
626 EventType::Scalar { value, anchor, .. } if value == "a" => Some(anchor.clone()),
627 _ => None,
628 });
629 assert_eq!(
630 key_a,
631 Some(Some("k".to_string())),
632 "expected key 'a' anchor=Some(k); got {key_a:?}"
633 );
634 }
635
636 #[test]
640 fn test_double_comma_in_flow_seq_errors() {
641 let yaml = "---\n[ a, b, c, , ]\n";
642 let mut parser = BasicParser::new_eager(yaml.to_string());
643 let mut saw_error = false;
644 while parser.check_event() {
645 match parser.get_event() {
646 Ok(Some(_)) => {}
647 Ok(None) => break,
648 Err(_) => {
649 saw_error = true;
650 break;
651 }
652 }
653 }
654 assert!(saw_error, "Expected error on [a, b, c, , ]");
655 }
656
657 #[test]
662 fn test_block_entry_no_item_synthesises_empty_scalar() {
663 let yaml = "-\n";
664 let mut parser = BasicParser::new_eager(yaml.to_string());
665 let mut events = Vec::new();
666 while parser.check_event() {
667 match parser.get_event() {
668 Ok(Some(event)) => events.push(event),
669 Ok(None) => break,
670 Err(e) => panic!("parser error: {e:?}"),
671 }
672 }
673 let scalars: Vec<(String, bool)> = events
674 .iter()
675 .filter_map(|e| match &e.event_type {
676 EventType::Scalar {
677 value,
678 plain_implicit,
679 ..
680 } => Some((value.clone(), *plain_implicit)),
681 _ => None,
682 })
683 .collect();
684 assert_eq!(
685 scalars,
686 vec![(String::new(), true)],
687 "Expected one implicit empty scalar item; got {scalars:?}"
688 );
689 }
690
691 #[test]
695 fn test_mixed_indent_widths_are_legal() {
696 let yaml = "a:\n b: 1\nx:\n y: 2\n";
697 let mut parser = BasicParser::new_eager(yaml.to_string());
698 let mut events = Vec::new();
699 while parser.check_event() {
700 match parser.get_event() {
701 Ok(Some(event)) => events.push(event),
702 Ok(None) => break,
703 Err(e) => panic!("parser error: {e:?}"),
704 }
705 }
706 let scalars: Vec<String> = events
707 .iter()
708 .filter_map(|e| match &e.event_type {
709 EventType::Scalar { value, .. } => Some(value.clone()),
710 _ => None,
711 })
712 .collect();
713 assert_eq!(
714 scalars,
715 vec!["a", "b", "1", "x", "y", "2"],
716 "Got scalars: {scalars:?}"
717 );
718 }
719
720 #[test]
725 fn test_alias_key_inside_anchored_mapping_value() {
726 let yaml = "anc: &a v\nouter: &node3\n *a : scalar3\n";
727 let mut parser = BasicParser::new_eager(yaml.to_string());
728 let mut events = Vec::new();
729 while parser.check_event() {
730 match parser.get_event() {
731 Ok(Some(event)) => events.push(event),
732 Ok(None) => break,
733 Err(e) => panic!("parser error: {e:?}"),
734 }
735 }
736 let mapping_anchors: Vec<Option<String>> = events
737 .iter()
738 .filter_map(|e| match &e.event_type {
739 EventType::MappingStart { anchor, .. } => Some(anchor.clone()),
740 _ => None,
741 })
742 .collect();
743 assert_eq!(
745 mapping_anchors,
746 vec![None, Some("node3".to_string())],
747 "expected [None, Some(node3)]; got {mapping_anchors:?}"
748 );
749 let alias_present = events
751 .iter()
752 .any(|e| matches!(&e.event_type, EventType::Alias { anchor } if anchor == "a"));
753 assert!(alias_present, "Expected Alias *a; events: {events:?}");
754 }
755
756 #[test]
760 fn test_leading_colon_implies_empty_key() {
761 let yaml = ": a\n: b\n";
762 let mut parser = BasicParser::new_eager(yaml.to_string());
763 let mut events = Vec::new();
764 while parser.check_event() {
765 if let Ok(Some(event)) = parser.get_event() {
766 events.push(event);
767 } else {
768 break;
769 }
770 }
771 assert!(
772 events
773 .iter()
774 .any(|e| matches!(e.event_type, EventType::MappingStart { .. })),
775 "Expected MappingStart for leading-colon mapping; events: {events:?}"
776 );
777 let scalars: Vec<String> = events
778 .iter()
779 .filter_map(|e| match &e.event_type {
780 EventType::Scalar { value, .. } => Some(value.clone()),
781 _ => None,
782 })
783 .collect();
784 assert_eq!(
785 scalars,
786 vec![
787 String::new(),
788 "a".to_string(),
789 String::new(),
790 "b".to_string(),
791 ],
792 "Expected empty-key/value pairs; got {scalars:?}"
793 );
794 }
795
796 #[test]
801 fn test_consecutive_complex_keys_emit_empty_values() {
802 let yaml = "? a\n? b\n";
803 let mut parser = BasicParser::new_eager(yaml.to_string());
804 let mut scalars = Vec::new();
805 while parser.check_event() {
806 if let Ok(Some(event)) = parser.get_event() {
807 if let EventType::Scalar { value, .. } = event.event_type {
808 scalars.push(value);
809 }
810 } else {
811 break;
812 }
813 }
814 assert_eq!(
815 scalars,
816 vec![
817 "a".to_string(),
818 String::new(),
819 "b".to_string(),
820 String::new()
821 ],
822 "Expected key/empty/key/empty pattern; got {scalars:?}"
823 );
824 }
825
826 #[test]
831 fn test_plain_scalar_key_may_contain_inner_colons() {
832 let yaml = "ab::cd: value\n";
833 let mut parser = BasicParser::new_eager(yaml.to_string());
834 let mut events = Vec::new();
835 while parser.check_event() {
836 if let Ok(Some(event)) = parser.get_event() {
837 events.push(event);
838 } else {
839 break;
840 }
841 }
842 let has_mapping = events
843 .iter()
844 .any(|e| matches!(e.event_type, EventType::MappingStart { .. }));
845 assert!(
846 has_mapping,
847 "Plain scalar `ab::cd: value` should open a mapping; events were {events:?}"
848 );
849 let scalars: Vec<&str> = events
850 .iter()
851 .filter_map(|e| match &e.event_type {
852 EventType::Scalar { value, .. } => Some(value.as_str()),
853 _ => None,
854 })
855 .collect();
856 assert_eq!(
857 scalars,
858 vec!["ab::cd", "value"],
859 "key/value split incorrect"
860 );
861 }
862
863 #[test]
864 fn test_explicit_type_tags() {
865 let yaml_with_tags = r"
866string_value: !!str 42
867int_value: !!int '123'
868float_value: !!float '3.14'
869bool_value: !!bool 'yes'
870null_value: !!null 'something'
871";
872
873 let mut parser = BasicParser::new_eager(yaml_with_tags.to_string());
874
875 let mut events = Vec::new();
876 while parser.check_event() {
877 if let Ok(Some(event)) = parser.get_event() {
878 events.push(event);
879 } else {
880 break;
881 }
882 }
883
884 let tagged_scalars: Vec<_> = events
886 .iter()
887 .filter_map(|e| {
888 if let EventType::Scalar { value, tag, .. } = &e.event_type {
889 Some((value.as_str(), tag.as_ref()?))
890 } else {
891 None
892 }
893 })
894 .collect();
895
896 assert!(!tagged_scalars.is_empty(), "Should find tagged scalars");
897
898 let str_scalar = tagged_scalars.iter().find(|(value, _)| *value == "42");
900 if let Some((_, tag)) = str_scalar {
901 assert_eq!(
902 *tag, "tag:yaml.org,2002:str",
903 "String tag should be normalized"
904 );
905 }
906
907 let int_scalar = tagged_scalars.iter().find(|(value, _)| *value == "123");
908 if let Some((_, tag)) = int_scalar {
909 assert_eq!(
910 *tag, "tag:yaml.org,2002:int",
911 "Int tag should be normalized"
912 );
913 }
914 }
915
916 #[test]
917 fn test_collection_type_tags() {
918 let yaml_with_collection_tags = r"
919explicit_sequence: !!seq [a, b, c]
920explicit_mapping: !!map {key: value}
921";
922
923 let mut parser = BasicParser::new_eager(yaml_with_collection_tags.to_string());
924
925 let mut events = Vec::new();
926 while parser.check_event() {
927 if let Ok(Some(event)) = parser.get_event() {
928 events.push(event);
929 } else {
930 break;
931 }
932 }
933
934 let tagged_seq = events.iter().find(|e| {
936 if let EventType::SequenceStart { tag, .. } = &e.event_type {
937 tag.as_ref().map_or(false, |t| t == "tag:yaml.org,2002:seq")
938 } else {
939 false
940 }
941 });
942
943 let tagged_map = events.iter().find(|e| {
944 if let EventType::MappingStart { tag, .. } = &e.event_type {
945 tag.as_ref().map_or(false, |t| t == "tag:yaml.org,2002:map")
946 } else {
947 false
948 }
949 });
950
951 assert!(tagged_seq.is_some(), "Should find tagged sequence");
952 assert!(tagged_map.is_some(), "Should find tagged mapping");
953 }
954
955 #[test]
956 fn test_tag_scanner() {
957 let yaml_with_various_tags = "value: !!str hello\nother: !custom tag\nshort: !int 42";
958
959 let mut scanner = BasicScanner::new_eager(yaml_with_various_tags.to_string());
960
961 let mut tag_tokens = Vec::new();
962 while scanner.check_token() {
963 if let Ok(Some(token)) = scanner.get_token() {
964 if let TokenType::Tag(tag) = &token.token_type {
965 tag_tokens.push(tag.clone());
966 }
967 } else {
968 break;
969 }
970 }
971
972 assert!(!tag_tokens.is_empty(), "Should find tag tokens");
973 assert!(
974 tag_tokens.iter().any(|t| t == "!!str"),
975 "Should find !!str tag"
976 );
977 assert!(
978 tag_tokens.iter().any(|t| t == "!custom"),
979 "Should preserve custom tags"
980 );
981 assert!(
982 tag_tokens.iter().any(|t| t == "!int"),
983 "Should find !int tag"
984 );
985 }
986
987 #[test]
988 fn test_streaming_parser() {
989 let yaml = r"
990items:
991 - name: first
992 value: 1
993 - name: second
994 value: 2
995";
996
997 let mut streaming_parser = BasicParser::new(yaml.to_string());
999 let mut stream_events = Vec::new();
1000
1001 while streaming_parser.check_event() {
1003 if let Ok(Some(event)) = streaming_parser.get_event() {
1004 stream_events.push(event);
1005 } else {
1006 break;
1007 }
1008 }
1009
1010 let mut eager_parser = BasicParser::new_eager(yaml.to_string());
1012 let mut eager_events = Vec::new();
1013
1014 while eager_parser.check_event() {
1015 if let Ok(Some(event)) = eager_parser.get_event() {
1016 eager_events.push(event);
1017 } else {
1018 break;
1019 }
1020 }
1021
1022 let has_mapping_start = stream_events
1025 .iter()
1026 .any(|e| matches!(e.event_type, EventType::MappingStart { .. }));
1027 let has_scalars = stream_events
1028 .iter()
1029 .any(|e| matches!(e.event_type, EventType::Scalar { .. }));
1030
1031 assert!(
1032 stream_events.len() > 0,
1033 "Streaming parser should generate events"
1034 );
1035 assert!(has_mapping_start, "Should have mapping start events");
1036 assert!(has_scalars, "Should have scalar events");
1037
1038 let eager_has_sequence = eager_events
1040 .iter()
1041 .any(|e| matches!(e.event_type, EventType::SequenceStart { .. }));
1042 assert!(
1043 eager_has_sequence,
1044 "Eager parser should have sequence start events"
1045 );
1046 }
1047
1048 #[test]
1049 fn test_complex_yaml_document() {
1050 let complex_yaml = r"
1051# Configuration for a web service
1052service:
1053 name: my-web-service
1054 version: &version '2.1.0'
1055
1056 # Server configuration
1057 server:
1058 host: localhost
1059 port: 8080
1060 ssl: true
1061
1062 # Database connections
1063 databases:
1064 primary: &primary_db
1065 driver: postgresql
1066 host: db.example.com
1067 port: 5432
1068 name: myapp_prod
1069
1070 cache:
1071 driver: redis
1072 host: cache.example.com
1073 port: 6379
1074
1075 # Feature flags with explicit types
1076 features:
1077 new_ui: !!bool true
1078 max_connections: !!int 100
1079 timeout: !!float 30.5
1080
1081 # Deployment environments
1082 environments:
1083 - name: development
1084 database: *primary_db
1085 debug: true
1086
1087 - name: staging
1088 database: *primary_db
1089 debug: false
1090
1091 - name: production
1092 database: *primary_db
1093 debug: false
1094
1095 # Multi-line configurations
1096 nginx_config: |
1097 server {
1098 listen 80;
1099 server_name example.com;
1100 location / {
1101 proxy_pass http://localhost:8080;
1102 }
1103 }
1104
1105 description: >
1106 This is a long description that will be
1107 folded into a single line when parsed,
1108 making it easier to read in the YAML file.
1109";
1110
1111 let mut parser = BasicParser::new_eager(complex_yaml.to_string());
1112 let mut events = Vec::new();
1113
1114 while parser.check_event() {
1115 if let Ok(Some(event)) = parser.get_event() {
1116 events.push(event);
1117 } else {
1118 break;
1119 }
1120 }
1121
1122 assert!(
1124 events.len() > 20,
1125 "Complex YAML should generate many events"
1126 );
1127
1128 let has_mapping_starts = events
1130 .iter()
1131 .filter(|e| matches!(e.event_type, EventType::MappingStart { .. }))
1132 .count();
1133 let has_sequence_starts = events
1134 .iter()
1135 .filter(|e| matches!(e.event_type, EventType::SequenceStart { .. }))
1136 .count();
1137 let has_scalars = events
1138 .iter()
1139 .filter(|e| matches!(e.event_type, EventType::Scalar { .. }))
1140 .count();
1141 let has_aliases = events
1142 .iter()
1143 .filter(|e| matches!(e.event_type, EventType::Alias { .. }))
1144 .count();
1145
1146 assert!(
1147 has_mapping_starts > 0,
1148 "Should have mapping starts (found: {})",
1149 has_mapping_starts
1150 );
1151 assert!(has_sequence_starts > 0, "Should have sequence starts");
1152 assert!(has_scalars > 10, "Should have many scalars");
1153 assert!(has_aliases > 0, "Should have aliases");
1154
1155 let anchored_scalars = events
1157 .iter()
1158 .filter(|e| {
1159 if let EventType::Scalar { anchor, .. } = &e.event_type {
1160 anchor.is_some()
1161 } else {
1162 false
1163 }
1164 })
1165 .count();
1166 assert!(anchored_scalars > 0, "Should have anchored scalars");
1167
1168 let tagged_scalars = events
1170 .iter()
1171 .filter(|e| {
1172 if let EventType::Scalar { tag, .. } = &e.event_type {
1173 tag.is_some()
1174 } else {
1175 false
1176 }
1177 })
1178 .count();
1179 assert!(tagged_scalars > 0, "Should have tagged scalars");
1180
1181 let literal_scalars = events
1183 .iter()
1184 .filter(|e| {
1185 if let EventType::Scalar { style, .. } = &e.event_type {
1186 matches!(style, parser::ScalarStyle::Literal)
1187 } else {
1188 false
1189 }
1190 })
1191 .count();
1192
1193 let folded_scalars = events
1194 .iter()
1195 .filter(|e| {
1196 if let EventType::Scalar { style, .. } = &e.event_type {
1197 matches!(style, parser::ScalarStyle::Folded)
1198 } else {
1199 false
1200 }
1201 })
1202 .count();
1203
1204 assert!(literal_scalars > 0, "Should have literal block scalars");
1205 assert!(folded_scalars > 0, "Should have folded block scalars");
1206 }
1207
1208 #[test]
1209 fn test_yaml_edge_cases() {
1210 let edge_cases = vec![
1212 ("", "empty document"),
1214 ("# Just a comment\n# Another comment", "comment only"),
1216 ("key: ~\nother: null\nthird:", "null values"),
1218 ("yes: true\nno: false\nmaybe: !!bool yes", "boolean values"),
1220 (
1222 "decimal: 123\noctal: 0o123\nhex: 0x123\nfloat: 1.23e4",
1223 "number formats",
1224 ),
1225 ("empty_list: []\nempty_dict: {}", "empty collections"),
1227 ("a: {b: {c: {d: value}}}", "deep nesting"),
1229 ];
1230
1231 for (yaml_content, description) in edge_cases {
1232 let mut parser = BasicParser::new_eager(yaml_content.to_string());
1233 let mut events = Vec::new();
1234
1235 while parser.check_event() {
1236 if let Ok(Some(event)) = parser.get_event() {
1237 events.push(event);
1238 } else {
1239 break;
1240 }
1241 }
1242
1243 assert!(
1245 events.len() >= 2,
1246 "Failed parsing {}: should have at least stream events",
1247 description
1248 );
1249
1250 let first_event = &events[0];
1251 let last_event = &events[events.len() - 1];
1252
1253 assert!(
1254 matches!(first_event.event_type, EventType::StreamStart),
1255 "Failed {}: should start with StreamStart",
1256 description
1257 );
1258 assert!(
1259 matches!(last_event.event_type, EventType::StreamEnd),
1260 "Failed {}: should end with StreamEnd",
1261 description
1262 );
1263 }
1264 }
1265
1266 #[test]
1267 fn test_round_trip_scalars() {
1268 let yaml = Yaml::new();
1269
1270 let test_values = vec![
1272 Value::Null,
1273 Value::Bool(true),
1274 Value::Bool(false),
1275 Value::Int(42),
1276 Value::String("hello".to_string()),
1277 ];
1278
1279 for original in test_values {
1280 if let Ok(yaml_str) = yaml.dump_str(&original) {
1282 if let Ok(round_trip) = yaml.load_str(&yaml_str) {
1283 assert_eq!(
1284 original, round_trip,
1285 "Round-trip failed for {:?}. YAML: {}",
1286 original, yaml_str
1287 );
1288 }
1289 }
1291 }
1293 }
1294
1295 #[test]
1296 fn test_round_trip_collections() {
1297 let yaml = Yaml::new();
1298
1299 let seq = Value::Sequence(vec![
1301 Value::Int(1),
1302 Value::String("hello".to_string()),
1303 Value::Bool(true),
1304 ]);
1305
1306 let yaml_str = yaml.dump_str(&seq).expect("Failed to serialize sequence");
1307 let round_trip = yaml.load_str(&yaml_str).expect("Failed to parse sequence");
1308 assert_eq!(
1309 seq, round_trip,
1310 "Sequence round-trip failed. YAML: {}",
1311 yaml_str
1312 );
1313
1314 let mut map = indexmap::IndexMap::new();
1316 map.insert(
1317 Value::String("name".to_string()),
1318 Value::String("Alice".to_string()),
1319 );
1320 map.insert(Value::String("age".to_string()), Value::Int(30));
1321 map.insert(Value::String("active".to_string()), Value::Bool(true));
1322 let mapping = Value::Mapping(map);
1323
1324 let yaml_str = yaml
1325 .dump_str(&mapping)
1326 .expect("Failed to serialize mapping");
1327 let round_trip = yaml.load_str(&yaml_str).expect("Failed to parse mapping");
1328 assert_eq!(
1329 mapping, round_trip,
1330 "Mapping round-trip failed. YAML: {}",
1331 yaml_str
1332 );
1333 }
1334
1335 #[test]
1336 fn test_round_trip_nested_structure() {
1337 let yaml = Yaml::new();
1338
1339 let mut inner_map = indexmap::IndexMap::new();
1341 inner_map.insert(Value::String("x".to_string()), Value::Int(10));
1342 inner_map.insert(Value::String("y".to_string()), Value::Int(20));
1343
1344 let seq = Value::Sequence(vec![
1345 Value::String("first".to_string()),
1346 Value::String("second".to_string()),
1347 Value::Mapping(inner_map),
1348 ]);
1349
1350 let mut outer_map = indexmap::IndexMap::new();
1351 outer_map.insert(Value::String("items".to_string()), seq);
1352 outer_map.insert(Value::String("count".to_string()), Value::Int(3));
1353
1354 let original = Value::Mapping(outer_map);
1355
1356 let yaml_str = yaml
1357 .dump_str(&original)
1358 .expect("Failed to serialize nested structure");
1359 let round_trip = yaml
1360 .load_str(&yaml_str)
1361 .expect("Failed to parse nested structure");
1362
1363 assert_eq!(
1364 original, round_trip,
1365 "Nested structure round-trip failed. YAML: {}",
1366 yaml_str
1367 );
1368 }
1369
1370 #[test]
1371 fn test_round_trip_with_special_strings() {
1372 let yaml = Yaml::new();
1373
1374 let special_strings = vec![
1375 "null", "true", "false", "123", "3.14", "yes", "no", "on", "off", "", " spaced ", ];
1387
1388 for s in special_strings {
1389 let original = Value::String(s.to_string());
1390 let yaml_str = yaml
1391 .dump_str(&original)
1392 .expect("Failed to serialize special string");
1393 let round_trip = yaml
1394 .load_str(&yaml_str)
1395 .expect("Failed to parse special string");
1396
1397 assert_eq!(
1398 original, round_trip,
1399 "Special string round-trip failed for '{}'. YAML: {}",
1400 s, yaml_str
1401 );
1402 }
1403 }
1404
1405 #[test]
1406 fn test_round_trip_complex_yaml() {
1407 let yaml = Yaml::new();
1408
1409 let complex_yaml = r"
1411service:
1412 name: my-web-service
1413 version: '2.1.0'
1414 server:
1415 host: localhost
1416 port: 8080
1417 ssl: true
1418 features:
1419 new_ui: true
1420 max_connections: 100
1421 timeout: 30.5
1422";
1423
1424 let parsed = yaml
1426 .load_str(complex_yaml)
1427 .expect("Failed to parse complex YAML");
1428
1429 let serialized = yaml
1431 .dump_str(&parsed)
1432 .expect("Failed to serialize complex structure");
1433
1434 let round_trip = yaml
1436 .load_str(&serialized)
1437 .expect("Failed to parse round-trip");
1438
1439 assert_eq!(parsed, round_trip, "Complex YAML round-trip failed");
1441 }
1442
1443 #[test]
1444 fn test_anchor_alias_serialization() {
1445 let yaml = Yaml::new();
1446
1447 let shared_mapping = {
1449 let mut map = indexmap::IndexMap::new();
1450 map.insert(
1451 Value::String("name".to_string()),
1452 Value::String("shared".to_string()),
1453 );
1454 map.insert(Value::String("value".to_string()), Value::Int(42));
1455 Value::Mapping(map)
1456 };
1457
1458 let mut root_map = indexmap::IndexMap::new();
1460 root_map.insert(Value::String("first".to_string()), shared_mapping.clone());
1461 root_map.insert(Value::String("second".to_string()), shared_mapping.clone());
1462 root_map.insert(Value::String("third".to_string()), shared_mapping);
1463
1464 let root = Value::Mapping(root_map);
1465
1466 let serialized = yaml
1468 .dump_str(&root)
1469 .expect("Failed to serialize shared structure");
1470
1471 println!("Serialized with anchors/aliases:");
1472 println!("{}", serialized);
1473
1474 assert!(
1476 serialized.contains("&anchor"),
1477 "Should contain anchor definition"
1478 );
1479 assert!(
1480 serialized.contains("*anchor"),
1481 "Should contain alias reference"
1482 );
1483
1484 assert!(
1486 serialized.contains("first:") && serialized.contains("&anchor0"),
1487 "Should have anchored first mapping"
1488 );
1489 assert!(
1490 serialized.contains("second:") && serialized.contains("*anchor0"),
1491 "Should have aliased second mapping"
1492 );
1493 assert!(
1494 serialized.contains("third:") && serialized.contains("*anchor0"),
1495 "Should have aliased third mapping"
1496 );
1497 assert!(
1498 serialized.contains("name: shared"),
1499 "Should contain shared content"
1500 );
1501 assert!(
1502 serialized.contains("value: 42"),
1503 "Should contain shared value"
1504 );
1505 }
1506
1507 #[test]
1508 fn test_anchor_alias_with_sequences() {
1509 let yaml = Yaml::new();
1510
1511 let shared_sequence = Value::Sequence(vec![
1513 Value::String("item1".to_string()),
1514 Value::String("item2".to_string()),
1515 Value::Int(123),
1516 ]);
1517
1518 let mut root_map = indexmap::IndexMap::new();
1520 root_map.insert(Value::String("list1".to_string()), shared_sequence.clone());
1521 root_map.insert(Value::String("list2".to_string()), shared_sequence);
1522
1523 let root = Value::Mapping(root_map);
1524
1525 let serialized = yaml
1527 .dump_str(&root)
1528 .expect("Failed to serialize shared sequences");
1529
1530 println!("Serialized sequences with anchors/aliases:");
1531 println!("{}", serialized);
1532
1533 assert!(
1535 serialized.contains("&anchor"),
1536 "Should contain anchor for shared sequence"
1537 );
1538 assert!(
1539 serialized.contains("*anchor"),
1540 "Should contain alias for shared sequence"
1541 );
1542
1543 assert!(
1545 serialized.contains("list1:") && serialized.contains("&anchor0"),
1546 "Should have anchored sequence"
1547 );
1548 assert!(
1549 serialized.contains("list2:") && serialized.contains("*anchor0"),
1550 "Should have aliased sequence"
1551 );
1552 assert!(
1553 serialized.contains("- item1"),
1554 "Should contain sequence items"
1555 );
1556 assert!(
1557 serialized.contains("- 123"),
1558 "Should contain sequence values"
1559 );
1560 }
1561}