Skip to main content

rust_yaml/
lib.rs

1//! # rust-yaml
2//!
3//! A fast, safe YAML library for Rust - port of ruamel-yaml
4//!
5//! This library provides comprehensive YAML 1.2 support with focus on:
6//! - Security: Memory safety, no unsafe operations
7//! - Performance: Zero-copy parsing, efficient memory usage
8//! - Reliability: Comprehensive error handling, deterministic behavior
9//! - Maintainability: Clean architecture, extensive testing
10
11#![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
102// Re-exports for convenience
103pub 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
115// Re-export commonly used types from components
116pub 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// pub use profiling::{YamlProfiler, StringInterner, ObjectPool}; // Temporarily disabled
137
138#[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        // Find the mapping with anchor (the anchor is on the mapping, not a scalar)
194        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        // Find the alias event
208        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        // Find the literal scalar
239        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        // Find the folded scalar
281        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            // Folded scalars fold internal line breaks into spaces, but
293            // clip-mode chomping (the default) keeps exactly one trailing
294            // line break per YAML 1.2 §8.1.1.2.
295            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    /// Within a literal block scalar, leading whitespace beyond
362    /// `content_indent` is literal content — even on otherwise-blank
363    /// lines. yaml-test-suite case 6FWR.
364    #[test]
365    fn test_literal_blank_line_preserves_extra_indent_as_content() {
366        // content_indent = 1 (first content line " ab" has 1 leading space).
367        // Line 3 "  " has 2 leading spaces; one is indent, one is content.
368        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        // §8.1.3.2: empty lines adjacent to more-indented content
381        // preserve every break instead of collapsing.
382        let yaml = ">\n a b\n\n   c d\n";
383        // "a b" (Normal) → empty → "  c d" (MoreIndented):
384        // 1 empty + adjacent MoreIndented → 2 newlines.
385        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        // Normal-Normal, 1 empty line → 1 newline (one break folded out).
391        let yaml = ">\n a\n\n b\n";
392        assert_eq!(parse_scalar_value(yaml), "a\nb\n");
393    }
394
395    /// In `? key\n: anchored-value\n: next-value`, the second `: value`
396    /// is the value for the previous key — not a new mapping entry.
397    /// The "this scalar is a new key" heuristic must NOT fire when the
398    /// scalar shares a line with the most recent `:` token. See
399    /// yaml-test-suite case 6M2F.
400    #[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    /// An anchor immediately before an implicit mapping key attaches to
435    /// the key scalar, not to the surrounding mapping (§6.9.2). See
436    /// yaml-test-suite case ZH7C.
437    #[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    /// `top1: &node1\n  &k1 key1: one` — `&node1` belongs to the inner
470    /// mapping (value of `top1`), and `&k1` belongs to the key `key1`
471    /// inside that mapping. These are two different nodes, so the parser
472    /// must NOT raise "more than one anchor". yaml-test-suite 7BMT.
473    #[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        // Outer mapping has no anchor; inner mapping has &node1.
486        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        // Inner key scalar "key1" carries anchor &k1.
499        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    /// A flow collection (`[ ]` or `{ }`) at line-start followed by `:`
511    /// on the same line is an implicit key of a block mapping. The block
512    /// mapping must open at the column of the flow-open token. yaml-test-
513    /// suite LX3P, 4FJ6, M2N8/01.
514    #[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    /// A nested block sequence (`- - x`) that continues on the next line
561    /// (`  - y`) should keep the inner sequence open. yaml-test-suite
562    /// 3ALJ, 57H4, 6BCT, W42U.
563    #[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    /// An anchor on its own line (no key immediately after on the same
601    /// line) belongs to the surrounding collection, not to a key. So
602    /// for `&m\n&k a: b`, &m attaches to the outer mapping and &k to
603    /// the inner key. yaml-test-suite 6BFJ, 9KAX.
604    #[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    /// YAML 1.2 §7.4: consecutive `,` separators in a flow collection
637    /// (e.g. `[a, , b]`, `[a, b, , ]`) are invalid — every comma must
638    /// terminate a preceding entry. yaml-test-suite CTN5.
639    #[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    /// A block-entry marker `-` without a following item denotes an
658    /// implicit empty scalar item. Two `- ` markers in a row mean the
659    /// first item is empty, and a single `-` followed by EOF/BlockEnd
660    /// means the only item is empty. yaml-test-suite SM9W cluster.
661    #[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    /// YAML 1.2 §6.1 allows mixed indent widths: e.g. one key uses 2-space
692    /// indent, a sibling uses 3-space. As long as children indent FURTHER
693    /// than parents, any positive amount works. yaml-test-suite 6HB6 et al.
694    #[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    /// `top3: &node3\n  *alias1 : v` — `&node3` belongs to the inner
721    /// mapping; `*alias1` is the inner key (already-defined alias). The
722    /// parser must NOT raise "Alias may not have an anchor or tag".
723    /// yaml-test-suite 26DV.
724    #[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        // outer mapping (no anchor), inner mapping with &node3
744        assert_eq!(
745            mapping_anchors,
746            vec![None, Some("node3".to_string())],
747            "expected [None, Some(node3)]; got {mapping_anchors:?}"
748        );
749        // Confirm an Alias event exists for *a as a key.
750        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    /// A line beginning with `:` denotes a mapping entry with an implicit
757    /// empty key. The parser must open a block mapping (if not already in
758    /// one) and synthesise the empty key. yaml-test-suite case 2JQS.
759    #[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    /// Two `? key` markers in a row mean the first key has no value.
797    /// The parser must synthesise an implicit empty scalar between them
798    /// so the mapping has an even number of children. See yaml-test-suite
799    /// case 7W2P.
800    #[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    /// Plain scalars may legally contain a `:` that is *not* followed by
827    /// whitespace (§7.3.3). The scanner must scan past such colons and
828    /// only treat a `: ` (colon + whitespace) as a key/value separator.
829    /// See yaml-test-suite case 8CWC.
830    #[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        // Find scalars with tags
885        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        // Verify specific tags are normalized
899        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        // Find collections with tags
935        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        // Test streaming (lazy) parser
998        let mut streaming_parser = BasicParser::new(yaml.to_string());
999        let mut stream_events = Vec::new();
1000
1001        // Events are generated on demand
1002        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        // Test eager parser for comparison
1011        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        // For now, just verify that streaming parser produces some meaningful events
1023        // Full streaming optimization is a complex feature requiring more architecture work
1024        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        // Verify eager parser works fully
1039        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        // Verify we parsed a complex document successfully
1123        assert!(
1124            events.len() > 20,
1125            "Complex YAML should generate many events"
1126        );
1127
1128        // Check for different types of events
1129        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        // Check for anchored values
1156        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        // Check for tagged values
1169        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        // Check for block scalar styles
1182        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        // Test various edge cases and special syntax
1211        let edge_cases = vec![
1212            // Empty document
1213            ("", "empty document"),
1214            // Document with only comments
1215            ("# Just a comment\n# Another comment", "comment only"),
1216            // Null values
1217            ("key: ~\nother: null\nthird:", "null values"),
1218            // Boolean variations
1219            ("yes: true\nno: false\nmaybe: !!bool yes", "boolean values"),
1220            // Number formats
1221            (
1222                "decimal: 123\noctal: 0o123\nhex: 0x123\nfloat: 1.23e4",
1223                "number formats",
1224            ),
1225            // Empty collections
1226            ("empty_list: []\nempty_dict: {}", "empty collections"),
1227            // Nested structures
1228            ("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            // Every YAML should at least have StreamStart and StreamEnd
1244            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        // Simplified test values for faster execution
1271        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            // Only test if both serialize and parse succeed
1281            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                // If parsing fails, that's ok - some features may not be implemented
1290            }
1291            // If serialization fails, that's ok - some features may not be implemented
1292        }
1293    }
1294
1295    #[test]
1296    fn test_round_trip_collections() {
1297        let yaml = Yaml::new();
1298
1299        // Test sequences
1300        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        // Test mappings
1315        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        // Create nested structure: mapping containing sequences and mappings
1340        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",       // Should be quoted
1376            "true",       // Should be quoted
1377            "false",      // Should be quoted
1378            "123",        // Should be quoted
1379            "3.14",       // Should be quoted
1380            "yes",        // Should be quoted
1381            "no",         // Should be quoted
1382            "on",         // Should be quoted
1383            "off",        // Should be quoted
1384            "",           // Empty string, should be quoted
1385            "  spaced  ", // String with spaces, should be quoted
1386        ];
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        // Test with the complex YAML from our integration test
1410        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        // Parse the original
1425        let parsed = yaml
1426            .load_str(complex_yaml)
1427            .expect("Failed to parse complex YAML");
1428
1429        // Serialize it
1430        let serialized = yaml
1431            .dump_str(&parsed)
1432            .expect("Failed to serialize complex structure");
1433
1434        // Parse the serialized version
1435        let round_trip = yaml
1436            .load_str(&serialized)
1437            .expect("Failed to parse round-trip");
1438
1439        // Should be the same
1440        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        // Create a structure with shared values that should generate anchors/aliases
1448        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        // Create a root structure that references the shared mapping multiple times
1459        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        // Serialize - should generate anchors/aliases for shared values
1467        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        // Check that anchors and aliases are generated
1475        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        // Verify the structure is correct
1485        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        // Create a shared sequence
1512        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        // Create a structure that reuses the sequence
1519        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        // Serialize
1526        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        // Should contain anchor/alias for sequences
1534        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        // Verify the structure
1544        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}