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#![allow(clippy::collapsible_if)]
75
76pub mod composer;
77pub mod composer_borrowed;
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 serializer;
90#[cfg(feature = "async")]
91pub mod streaming_async;
92pub mod streaming_enhanced;
93pub mod tag;
94pub mod value;
95pub mod value_borrowed;
96pub mod yaml;
97pub mod zero_copy_value;
98pub mod zerocopy;
99
100// Re-exports for convenience
101pub use error::{Error, Result};
102pub use limits::{Limits, ResourceStats, ResourceTracker};
103pub use position::Position;
104pub use scanner::QuoteStyle;
105pub use value::{CommentedValue, Comments, IndentStyle, Style, Value};
106pub use value_borrowed::BorrowedValue;
107pub use yaml::{LoaderType, Yaml, YamlConfig};
108pub use zero_copy_value::OptimizedValue;
109
110// Re-export commonly used types from components
111pub use composer::{BasicComposer, Composer};
112pub use composer_borrowed::{BorrowedComposer, ZeroCopyComposer};
113pub use composer_optimized::{OptimizedComposer, ReducedAllocComposer};
114pub use constructor::{Constructor, SafeConstructor};
115pub use emitter::{BasicEmitter, Emitter};
116pub use parser::{
117    BasicParser, Event, EventType, Parser, StreamingConfig, StreamingParser, StreamingStats,
118};
119pub use representer::{Representer, SafeRepresenter};
120pub use resolver::{BasicResolver, Resolver};
121pub use scanner::{BasicScanner, Scanner, Token, TokenType};
122pub use serializer::{BasicSerializer, Serializer};
123pub use streaming_enhanced::{
124    stream_from_file, stream_from_string, StreamConfig, StreamingYamlParser,
125};
126pub use zerocopy::{ScannerStats, TokenPool, ZeroScanner, ZeroString, ZeroToken, ZeroTokenType};
127// pub use profiling::{YamlProfiler, StringInterner, ObjectPool}; // Temporarily disabled
128
129#[cfg(feature = "serde")]
130pub mod serde_integration;
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::parser::ScalarStyle;
136
137    #[test]
138    fn test_basic_functionality() {
139        let yaml = Yaml::new();
140        let value = yaml.load_str("42").unwrap();
141        assert_eq!(value, Value::Int(42));
142    }
143
144    #[test]
145    fn test_error_creation() {
146        let pos = Position::new();
147        let error = Error::parse(pos, "test error");
148        assert!(error.to_string().contains("test error"));
149    }
150
151    #[test]
152    fn test_value_types() {
153        assert_eq!(Value::Null, Value::Null);
154        assert_eq!(Value::Bool(true), Value::Bool(true));
155        assert_eq!(Value::Int(42), Value::Int(42));
156        assert_eq!(Value::Float(3.14), Value::Float(3.14));
157        assert_eq!(
158            Value::String("test".to_string()),
159            Value::String("test".to_string())
160        );
161    }
162
163    #[test]
164    fn test_anchor_alias_parsing() {
165        let yaml_with_anchors = r"
166base: &base
167  name: test
168  value: 42
169
170prod: *base
171";
172
173        let mut parser = BasicParser::new_eager(yaml_with_anchors.to_string());
174
175        let mut events = Vec::new();
176        while parser.check_event() {
177            if let Ok(Some(event)) = parser.get_event() {
178                events.push(event);
179            } else {
180                break;
181            }
182        }
183
184        // Find the mapping with anchor (the anchor is on the mapping, not a scalar)
185        let base_mapping = events.iter().find(|e| {
186            if let EventType::MappingStart { anchor, .. } = &e.event_type {
187                anchor.as_ref().map_or(false, |a| a == "base")
188            } else {
189                false
190            }
191        });
192
193        assert!(
194            base_mapping.is_some(),
195            "Should find mapping with 'base' anchor"
196        );
197
198        // Find the alias event
199        let alias_event = events
200            .iter()
201            .find(|e| matches!(e.event_type, EventType::Alias { .. }));
202
203        assert!(alias_event.is_some(), "Should find alias event");
204
205        if let EventType::Alias { anchor } = &alias_event.unwrap().event_type {
206            assert_eq!(anchor, "base", "Alias should reference 'base'");
207        }
208    }
209
210    #[test]
211    fn test_literal_block_scalar() {
212        let yaml_literal = r"literal: |
213  This text contains
214  multiple lines
215  with preserved newlines
216";
217
218        let mut parser = BasicParser::new_eager(yaml_literal.to_string());
219
220        let mut events = Vec::new();
221        while parser.check_event() {
222            if let Ok(Some(event)) = parser.get_event() {
223                events.push(event);
224            } else {
225                break;
226            }
227        }
228
229        // Find the literal scalar
230        let literal_scalar = events.iter().find(|e| {
231            if let EventType::Scalar { value, style, .. } = &e.event_type {
232                *style == ScalarStyle::Literal && value.contains("This text contains")
233            } else {
234                false
235            }
236        });
237
238        assert!(literal_scalar.is_some(), "Should find literal scalar");
239
240        if let EventType::Scalar { value, .. } = &literal_scalar.unwrap().event_type {
241            assert!(
242                value.contains('\n'),
243                "Literal scalar should preserve newlines"
244            );
245            assert!(
246                value.contains("This text contains"),
247                "Should contain the literal text"
248            );
249        }
250    }
251
252    #[test]
253    fn test_folded_block_scalar() {
254        let yaml_folded = r"folded: >
255  This text will be
256  folded into a
257  single line
258";
259
260        let mut parser = BasicParser::new_eager(yaml_folded.to_string());
261
262        let mut events = Vec::new();
263        while parser.check_event() {
264            if let Ok(Some(event)) = parser.get_event() {
265                events.push(event);
266            } else {
267                break;
268            }
269        }
270
271        // Find the folded scalar
272        let folded_scalar = events.iter().find(|e| {
273            if let EventType::Scalar { value, style, .. } = &e.event_type {
274                *style == ScalarStyle::Folded && value.contains("This text will be")
275            } else {
276                false
277            }
278        });
279
280        assert!(folded_scalar.is_some(), "Should find folded scalar");
281
282        if let EventType::Scalar { value, .. } = &folded_scalar.unwrap().event_type {
283            // Folded scalars should have spaces instead of newlines
284            assert!(
285                !value.contains('\n'),
286                "Folded scalar should not preserve newlines"
287            );
288            assert!(
289                value.contains("This text will be folded into a single line"),
290                "Should fold the text"
291            );
292        }
293    }
294
295    #[test]
296    fn test_explicit_type_tags() {
297        let yaml_with_tags = r"
298string_value: !!str 42
299int_value: !!int '123'
300float_value: !!float '3.14'
301bool_value: !!bool 'yes'
302null_value: !!null 'something'
303";
304
305        let mut parser = BasicParser::new_eager(yaml_with_tags.to_string());
306
307        let mut events = Vec::new();
308        while parser.check_event() {
309            if let Ok(Some(event)) = parser.get_event() {
310                events.push(event);
311            } else {
312                break;
313            }
314        }
315
316        // Find scalars with tags
317        let tagged_scalars: Vec<_> = events
318            .iter()
319            .filter_map(|e| {
320                if let EventType::Scalar { value, tag, .. } = &e.event_type {
321                    Some((value.as_str(), tag.as_ref()?))
322                } else {
323                    None
324                }
325            })
326            .collect();
327
328        assert!(!tagged_scalars.is_empty(), "Should find tagged scalars");
329
330        // Verify specific tags are normalized
331        let str_scalar = tagged_scalars.iter().find(|(value, _)| *value == "42");
332        if let Some((_, tag)) = str_scalar {
333            assert_eq!(
334                *tag, "tag:yaml.org,2002:str",
335                "String tag should be normalized"
336            );
337        }
338
339        let int_scalar = tagged_scalars.iter().find(|(value, _)| *value == "123");
340        if let Some((_, tag)) = int_scalar {
341            assert_eq!(
342                *tag, "tag:yaml.org,2002:int",
343                "Int tag should be normalized"
344            );
345        }
346    }
347
348    #[test]
349    fn test_collection_type_tags() {
350        let yaml_with_collection_tags = r"
351explicit_sequence: !!seq [a, b, c]
352explicit_mapping: !!map {key: value}
353";
354
355        let mut parser = BasicParser::new_eager(yaml_with_collection_tags.to_string());
356
357        let mut events = Vec::new();
358        while parser.check_event() {
359            if let Ok(Some(event)) = parser.get_event() {
360                events.push(event);
361            } else {
362                break;
363            }
364        }
365
366        // Find collections with tags
367        let tagged_seq = events.iter().find(|e| {
368            if let EventType::SequenceStart { tag, .. } = &e.event_type {
369                tag.as_ref().map_or(false, |t| t == "tag:yaml.org,2002:seq")
370            } else {
371                false
372            }
373        });
374
375        let tagged_map = events.iter().find(|e| {
376            if let EventType::MappingStart { tag, .. } = &e.event_type {
377                tag.as_ref().map_or(false, |t| t == "tag:yaml.org,2002:map")
378            } else {
379                false
380            }
381        });
382
383        assert!(tagged_seq.is_some(), "Should find tagged sequence");
384        assert!(tagged_map.is_some(), "Should find tagged mapping");
385    }
386
387    #[test]
388    fn test_tag_scanner() {
389        let yaml_with_various_tags = "value: !!str hello\nother: !custom tag\nshort: !int 42";
390
391        let mut scanner = BasicScanner::new_eager(yaml_with_various_tags.to_string());
392
393        let mut tag_tokens = Vec::new();
394        while scanner.check_token() {
395            if let Ok(Some(token)) = scanner.get_token() {
396                if let TokenType::Tag(tag) = &token.token_type {
397                    tag_tokens.push(tag.clone());
398                }
399            } else {
400                break;
401            }
402        }
403
404        assert!(!tag_tokens.is_empty(), "Should find tag tokens");
405        assert!(
406            tag_tokens.iter().any(|t| t == "!!str"),
407            "Should find !!str tag"
408        );
409        assert!(
410            tag_tokens.iter().any(|t| t == "!custom"),
411            "Should preserve custom tags"
412        );
413        assert!(
414            tag_tokens.iter().any(|t| t == "!int"),
415            "Should find !int tag"
416        );
417    }
418
419    #[test]
420    fn test_streaming_parser() {
421        let yaml = r"
422items:
423  - name: first
424    value: 1
425  - name: second
426    value: 2
427";
428
429        // Test streaming (lazy) parser
430        let mut streaming_parser = BasicParser::new(yaml.to_string());
431        let mut stream_events = Vec::new();
432
433        // Events are generated on demand
434        while streaming_parser.check_event() {
435            if let Ok(Some(event)) = streaming_parser.get_event() {
436                stream_events.push(event);
437            } else {
438                break;
439            }
440        }
441
442        // Test eager parser for comparison
443        let mut eager_parser = BasicParser::new_eager(yaml.to_string());
444        let mut eager_events = Vec::new();
445
446        while eager_parser.check_event() {
447            if let Ok(Some(event)) = eager_parser.get_event() {
448                eager_events.push(event);
449            } else {
450                break;
451            }
452        }
453
454        // For now, just verify that streaming parser produces some meaningful events
455        // Full streaming optimization is a complex feature requiring more architecture work
456        let has_mapping_start = stream_events
457            .iter()
458            .any(|e| matches!(e.event_type, EventType::MappingStart { .. }));
459        let has_scalars = stream_events
460            .iter()
461            .any(|e| matches!(e.event_type, EventType::Scalar { .. }));
462
463        assert!(
464            stream_events.len() > 0,
465            "Streaming parser should generate events"
466        );
467        assert!(has_mapping_start, "Should have mapping start events");
468        assert!(has_scalars, "Should have scalar events");
469
470        // Verify eager parser works fully
471        let eager_has_sequence = eager_events
472            .iter()
473            .any(|e| matches!(e.event_type, EventType::SequenceStart { .. }));
474        assert!(
475            eager_has_sequence,
476            "Eager parser should have sequence start events"
477        );
478    }
479
480    #[test]
481    fn test_complex_yaml_document() {
482        let complex_yaml = r"
483# Configuration for a web service
484service:
485  name: my-web-service
486  version: &version '2.1.0'
487
488  # Server configuration
489  server:
490    host: localhost
491    port: 8080
492    ssl: true
493
494  # Database connections
495  databases:
496    primary: &primary_db
497      driver: postgresql
498      host: db.example.com
499      port: 5432
500      name: myapp_prod
501
502    cache:
503      driver: redis
504      host: cache.example.com
505      port: 6379
506
507  # Feature flags with explicit types
508  features:
509    new_ui: !!bool true
510    max_connections: !!int 100
511    timeout: !!float 30.5
512
513  # Deployment environments
514  environments:
515    - name: development
516      database: *primary_db
517      debug: true
518
519    - name: staging
520      database: *primary_db
521      debug: false
522
523    - name: production
524      database: *primary_db
525      debug: false
526
527  # Multi-line configurations
528  nginx_config: |
529    server {
530        listen 80;
531        server_name example.com;
532        location / {
533            proxy_pass http://localhost:8080;
534        }
535    }
536
537  description: >
538    This is a long description that will be
539    folded into a single line when parsed,
540    making it easier to read in the YAML file.
541";
542
543        let mut parser = BasicParser::new_eager(complex_yaml.to_string());
544        let mut events = Vec::new();
545
546        while parser.check_event() {
547            if let Ok(Some(event)) = parser.get_event() {
548                events.push(event);
549            } else {
550                break;
551            }
552        }
553
554        // Verify we parsed a complex document successfully
555        assert!(
556            events.len() > 20,
557            "Complex YAML should generate many events"
558        );
559
560        // Check for different types of events
561        let has_mapping_starts = events
562            .iter()
563            .filter(|e| matches!(e.event_type, EventType::MappingStart { .. }))
564            .count();
565        let has_sequence_starts = events
566            .iter()
567            .filter(|e| matches!(e.event_type, EventType::SequenceStart { .. }))
568            .count();
569        let has_scalars = events
570            .iter()
571            .filter(|e| matches!(e.event_type, EventType::Scalar { .. }))
572            .count();
573        let has_aliases = events
574            .iter()
575            .filter(|e| matches!(e.event_type, EventType::Alias { .. }))
576            .count();
577
578        assert!(
579            has_mapping_starts > 0,
580            "Should have mapping starts (found: {})",
581            has_mapping_starts
582        );
583        assert!(has_sequence_starts > 0, "Should have sequence starts");
584        assert!(has_scalars > 10, "Should have many scalars");
585        assert!(has_aliases > 0, "Should have aliases");
586
587        // Check for anchored values
588        let anchored_scalars = events
589            .iter()
590            .filter(|e| {
591                if let EventType::Scalar { anchor, .. } = &e.event_type {
592                    anchor.is_some()
593                } else {
594                    false
595                }
596            })
597            .count();
598        assert!(anchored_scalars > 0, "Should have anchored scalars");
599
600        // Check for tagged values
601        let tagged_scalars = events
602            .iter()
603            .filter(|e| {
604                if let EventType::Scalar { tag, .. } = &e.event_type {
605                    tag.is_some()
606                } else {
607                    false
608                }
609            })
610            .count();
611        assert!(tagged_scalars > 0, "Should have tagged scalars");
612
613        // Check for block scalar styles
614        let literal_scalars = events
615            .iter()
616            .filter(|e| {
617                if let EventType::Scalar { style, .. } = &e.event_type {
618                    matches!(style, parser::ScalarStyle::Literal)
619                } else {
620                    false
621                }
622            })
623            .count();
624
625        let folded_scalars = events
626            .iter()
627            .filter(|e| {
628                if let EventType::Scalar { style, .. } = &e.event_type {
629                    matches!(style, parser::ScalarStyle::Folded)
630                } else {
631                    false
632                }
633            })
634            .count();
635
636        assert!(literal_scalars > 0, "Should have literal block scalars");
637        assert!(folded_scalars > 0, "Should have folded block scalars");
638    }
639
640    #[test]
641    fn test_yaml_edge_cases() {
642        // Test various edge cases and special syntax
643        let edge_cases = vec![
644            // Empty document
645            ("", "empty document"),
646            // Document with only comments
647            ("# Just a comment\n# Another comment", "comment only"),
648            // Null values
649            ("key: ~\nother: null\nthird:", "null values"),
650            // Boolean variations
651            ("yes: true\nno: false\nmaybe: !!bool yes", "boolean values"),
652            // Number formats
653            (
654                "decimal: 123\noctal: 0o123\nhex: 0x123\nfloat: 1.23e4",
655                "number formats",
656            ),
657            // Empty collections
658            ("empty_list: []\nempty_dict: {}", "empty collections"),
659            // Nested structures
660            ("a: {b: {c: {d: value}}}", "deep nesting"),
661        ];
662
663        for (yaml_content, description) in edge_cases {
664            let mut parser = BasicParser::new_eager(yaml_content.to_string());
665            let mut events = Vec::new();
666
667            while parser.check_event() {
668                if let Ok(Some(event)) = parser.get_event() {
669                    events.push(event);
670                } else {
671                    break;
672                }
673            }
674
675            // Every YAML should at least have StreamStart and StreamEnd
676            assert!(
677                events.len() >= 2,
678                "Failed parsing {}: should have at least stream events",
679                description
680            );
681
682            let first_event = &events[0];
683            let last_event = &events[events.len() - 1];
684
685            assert!(
686                matches!(first_event.event_type, EventType::StreamStart),
687                "Failed {}: should start with StreamStart",
688                description
689            );
690            assert!(
691                matches!(last_event.event_type, EventType::StreamEnd),
692                "Failed {}: should end with StreamEnd",
693                description
694            );
695        }
696    }
697
698    #[test]
699    fn test_round_trip_scalars() {
700        let yaml = Yaml::new();
701
702        // Simplified test values for faster execution
703        let test_values = vec![
704            Value::Null,
705            Value::Bool(true),
706            Value::Bool(false),
707            Value::Int(42),
708            Value::String("hello".to_string()),
709        ];
710
711        for original in test_values {
712            // Only test if both serialize and parse succeed
713            if let Ok(yaml_str) = yaml.dump_str(&original) {
714                if let Ok(round_trip) = yaml.load_str(&yaml_str) {
715                    assert_eq!(
716                        original, round_trip,
717                        "Round-trip failed for {:?}. YAML: {}",
718                        original, yaml_str
719                    );
720                }
721                // If parsing fails, that's ok - some features may not be implemented
722            }
723            // If serialization fails, that's ok - some features may not be implemented
724        }
725    }
726
727    #[test]
728    fn test_round_trip_collections() {
729        let yaml = Yaml::new();
730
731        // Test sequences
732        let seq = Value::Sequence(vec![
733            Value::Int(1),
734            Value::String("hello".to_string()),
735            Value::Bool(true),
736        ]);
737
738        let yaml_str = yaml.dump_str(&seq).expect("Failed to serialize sequence");
739        let round_trip = yaml.load_str(&yaml_str).expect("Failed to parse sequence");
740        assert_eq!(
741            seq, round_trip,
742            "Sequence round-trip failed. YAML: {}",
743            yaml_str
744        );
745
746        // Test mappings
747        let mut map = indexmap::IndexMap::new();
748        map.insert(
749            Value::String("name".to_string()),
750            Value::String("Alice".to_string()),
751        );
752        map.insert(Value::String("age".to_string()), Value::Int(30));
753        map.insert(Value::String("active".to_string()), Value::Bool(true));
754        let mapping = Value::Mapping(map);
755
756        let yaml_str = yaml
757            .dump_str(&mapping)
758            .expect("Failed to serialize mapping");
759        let round_trip = yaml.load_str(&yaml_str).expect("Failed to parse mapping");
760        assert_eq!(
761            mapping, round_trip,
762            "Mapping round-trip failed. YAML: {}",
763            yaml_str
764        );
765    }
766
767    #[test]
768    fn test_round_trip_nested_structure() {
769        let yaml = Yaml::new();
770
771        // Create nested structure: mapping containing sequences and mappings
772        let mut inner_map = indexmap::IndexMap::new();
773        inner_map.insert(Value::String("x".to_string()), Value::Int(10));
774        inner_map.insert(Value::String("y".to_string()), Value::Int(20));
775
776        let seq = Value::Sequence(vec![
777            Value::String("first".to_string()),
778            Value::String("second".to_string()),
779            Value::Mapping(inner_map),
780        ]);
781
782        let mut outer_map = indexmap::IndexMap::new();
783        outer_map.insert(Value::String("items".to_string()), seq);
784        outer_map.insert(Value::String("count".to_string()), Value::Int(3));
785
786        let original = Value::Mapping(outer_map);
787
788        let yaml_str = yaml
789            .dump_str(&original)
790            .expect("Failed to serialize nested structure");
791        let round_trip = yaml
792            .load_str(&yaml_str)
793            .expect("Failed to parse nested structure");
794
795        assert_eq!(
796            original, round_trip,
797            "Nested structure round-trip failed. YAML: {}",
798            yaml_str
799        );
800    }
801
802    #[test]
803    fn test_round_trip_with_special_strings() {
804        let yaml = Yaml::new();
805
806        let special_strings = vec![
807            "null",       // Should be quoted
808            "true",       // Should be quoted
809            "false",      // Should be quoted
810            "123",        // Should be quoted
811            "3.14",       // Should be quoted
812            "yes",        // Should be quoted
813            "no",         // Should be quoted
814            "on",         // Should be quoted
815            "off",        // Should be quoted
816            "",           // Empty string, should be quoted
817            "  spaced  ", // String with spaces, should be quoted
818        ];
819
820        for s in special_strings {
821            let original = Value::String(s.to_string());
822            let yaml_str = yaml
823                .dump_str(&original)
824                .expect("Failed to serialize special string");
825            let round_trip = yaml
826                .load_str(&yaml_str)
827                .expect("Failed to parse special string");
828
829            assert_eq!(
830                original, round_trip,
831                "Special string round-trip failed for '{}'. YAML: {}",
832                s, yaml_str
833            );
834        }
835    }
836
837    #[test]
838    fn test_round_trip_complex_yaml() {
839        let yaml = Yaml::new();
840
841        // Test with the complex YAML from our integration test
842        let complex_yaml = r"
843service:
844  name: my-web-service
845  version: '2.1.0'
846  server:
847    host: localhost
848    port: 8080
849    ssl: true
850  features:
851    new_ui: true
852    max_connections: 100
853    timeout: 30.5
854";
855
856        // Parse the original
857        let parsed = yaml
858            .load_str(complex_yaml)
859            .expect("Failed to parse complex YAML");
860
861        // Serialize it
862        let serialized = yaml
863            .dump_str(&parsed)
864            .expect("Failed to serialize complex structure");
865
866        // Parse the serialized version
867        let round_trip = yaml
868            .load_str(&serialized)
869            .expect("Failed to parse round-trip");
870
871        // Should be the same
872        assert_eq!(parsed, round_trip, "Complex YAML round-trip failed");
873    }
874
875    #[test]
876    fn test_anchor_alias_serialization() {
877        let yaml = Yaml::new();
878
879        // Create a structure with shared values that should generate anchors/aliases
880        let shared_mapping = {
881            let mut map = indexmap::IndexMap::new();
882            map.insert(
883                Value::String("name".to_string()),
884                Value::String("shared".to_string()),
885            );
886            map.insert(Value::String("value".to_string()), Value::Int(42));
887            Value::Mapping(map)
888        };
889
890        // Create a root structure that references the shared mapping multiple times
891        let mut root_map = indexmap::IndexMap::new();
892        root_map.insert(Value::String("first".to_string()), shared_mapping.clone());
893        root_map.insert(Value::String("second".to_string()), shared_mapping.clone());
894        root_map.insert(Value::String("third".to_string()), shared_mapping);
895
896        let root = Value::Mapping(root_map);
897
898        // Serialize - should generate anchors/aliases for shared values
899        let serialized = yaml
900            .dump_str(&root)
901            .expect("Failed to serialize shared structure");
902
903        println!("Serialized with anchors/aliases:");
904        println!("{}", serialized);
905
906        // Check that anchors and aliases are generated
907        assert!(
908            serialized.contains("&anchor"),
909            "Should contain anchor definition"
910        );
911        assert!(
912            serialized.contains("*anchor"),
913            "Should contain alias reference"
914        );
915
916        // Verify the structure is correct
917        assert!(
918            serialized.contains("first:") && serialized.contains("&anchor0"),
919            "Should have anchored first mapping"
920        );
921        assert!(
922            serialized.contains("second:") && serialized.contains("*anchor0"),
923            "Should have aliased second mapping"
924        );
925        assert!(
926            serialized.contains("third:") && serialized.contains("*anchor0"),
927            "Should have aliased third mapping"
928        );
929        assert!(
930            serialized.contains("name: shared"),
931            "Should contain shared content"
932        );
933        assert!(
934            serialized.contains("value: 42"),
935            "Should contain shared value"
936        );
937    }
938
939    #[test]
940    fn test_anchor_alias_with_sequences() {
941        let yaml = Yaml::new();
942
943        // Create a shared sequence
944        let shared_sequence = Value::Sequence(vec![
945            Value::String("item1".to_string()),
946            Value::String("item2".to_string()),
947            Value::Int(123),
948        ]);
949
950        // Create a structure that reuses the sequence
951        let mut root_map = indexmap::IndexMap::new();
952        root_map.insert(Value::String("list1".to_string()), shared_sequence.clone());
953        root_map.insert(Value::String("list2".to_string()), shared_sequence);
954
955        let root = Value::Mapping(root_map);
956
957        // Serialize
958        let serialized = yaml
959            .dump_str(&root)
960            .expect("Failed to serialize shared sequences");
961
962        println!("Serialized sequences with anchors/aliases:");
963        println!("{}", serialized);
964
965        // Should contain anchor/alias for sequences
966        assert!(
967            serialized.contains("&anchor"),
968            "Should contain anchor for shared sequence"
969        );
970        assert!(
971            serialized.contains("*anchor"),
972            "Should contain alias for shared sequence"
973        );
974
975        // Verify the structure
976        assert!(
977            serialized.contains("list1:") && serialized.contains("&anchor0"),
978            "Should have anchored sequence"
979        );
980        assert!(
981            serialized.contains("list2:") && serialized.contains("*anchor0"),
982            "Should have aliased sequence"
983        );
984        assert!(
985            serialized.contains("- item1"),
986            "Should contain sequence items"
987        );
988        assert!(
989            serialized.contains("- 123"),
990            "Should contain sequence values"
991        );
992    }
993}