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