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_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
100pub 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
110pub 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#[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 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 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 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 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 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 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 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 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 let mut streaming_parser = BasicParser::new(yaml.to_string());
431 let mut stream_events = Vec::new();
432
433 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 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 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 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 assert!(
556 events.len() > 20,
557 "Complex YAML should generate many events"
558 );
559
560 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 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 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 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 let edge_cases = vec![
644 ("", "empty document"),
646 ("# Just a comment\n# Another comment", "comment only"),
648 ("key: ~\nother: null\nthird:", "null values"),
650 ("yes: true\nno: false\nmaybe: !!bool yes", "boolean values"),
652 (
654 "decimal: 123\noctal: 0o123\nhex: 0x123\nfloat: 1.23e4",
655 "number formats",
656 ),
657 ("empty_list: []\nempty_dict: {}", "empty collections"),
659 ("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 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 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 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 }
723 }
725 }
726
727 #[test]
728 fn test_round_trip_collections() {
729 let yaml = Yaml::new();
730
731 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 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 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", "true", "false", "123", "3.14", "yes", "no", "on", "off", "", " spaced ", ];
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 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 let parsed = yaml
858 .load_str(complex_yaml)
859 .expect("Failed to parse complex YAML");
860
861 let serialized = yaml
863 .dump_str(&parsed)
864 .expect("Failed to serialize complex structure");
865
866 let round_trip = yaml
868 .load_str(&serialized)
869 .expect("Failed to parse round-trip");
870
871 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 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 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 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 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 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 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 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 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 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 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}