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