1#[cfg(test)]
4use crate::scanner::Scanner;
5use crate::tag::TagResolver;
6use crate::{
7 parser::EventType, BasicParser, Error, Limits, Parser, Position, ResourceTracker, Result, Value,
8};
9use indexmap::IndexMap;
10use std::collections::HashMap;
11
12fn calculate_value_complexity(value: &Value) -> Result<usize> {
14 let mut complexity = 1usize;
15
16 match value {
17 Value::Sequence(seq) => {
18 complexity = complexity.saturating_add(seq.len());
19 for item in seq {
20 complexity = complexity.saturating_add(calculate_value_complexity(item)?);
21 }
22 }
23 Value::Mapping(map) => {
24 complexity = complexity.saturating_add(map.len().saturating_mul(2));
25 for (key, val) in map {
26 complexity = complexity.saturating_add(calculate_value_complexity(key)?);
27 complexity = complexity.saturating_add(calculate_value_complexity(val)?);
28 }
29 }
30 _ => {} }
32
33 Ok(complexity)
34}
35
36fn calculate_structure_depth(value: &Value) -> usize {
38 match value {
39 Value::Sequence(seq) => {
40 if seq.is_empty() {
41 1
42 } else {
43 1 + seq.iter().map(calculate_structure_depth).max().unwrap_or(0)
44 }
45 }
46 Value::Mapping(map) => {
47 if map.is_empty() {
48 1
49 } else {
50 1 + map
51 .values()
52 .map(calculate_structure_depth)
53 .max()
54 .unwrap_or(0)
55 }
56 }
57 _ => 1, }
59}
60
61pub trait Composer {
63 fn check_document(&self) -> bool;
65
66 fn compose_document(&mut self) -> Result<Option<Value>>;
71
72 fn position(&self) -> Position;
74
75 fn reset(&mut self);
77}
78
79#[derive(Debug)]
81pub struct BasicComposer {
82 parser: BasicParser,
83 position: Position,
84 anchors: HashMap<String, Value>,
85 limits: Limits,
86 resource_tracker: ResourceTracker,
87 alias_expansion_stack: Vec<String>,
88 current_depth: usize,
89 tag_resolver: TagResolver,
90}
91
92impl BasicComposer {
93 #[must_use]
95 pub fn new(input: String) -> Self {
96 Self::with_limits(input, Limits::default())
97 }
98
99 #[must_use]
101 pub fn with_limits(input: String, limits: Limits) -> Self {
102 Self {
103 parser: BasicParser::with_limits(input, limits.clone()),
104 position: Position::new(),
105 anchors: HashMap::new(),
106 limits,
107 resource_tracker: ResourceTracker::new(),
108 alias_expansion_stack: Vec::new(),
109 current_depth: 0,
110 tag_resolver: TagResolver::new(),
111 }
112 }
113
114 #[must_use]
116 pub fn new_eager(input: String) -> Self {
117 Self::new_eager_with_limits(input, Limits::default())
118 }
119
120 #[must_use]
122 pub fn new_eager_with_limits(input: String, limits: Limits) -> Self {
123 Self {
124 parser: BasicParser::new_eager_with_limits(input, limits.clone()),
125 position: Position::new(),
126 anchors: HashMap::new(),
127 limits,
128 resource_tracker: ResourceTracker::new(),
129 alias_expansion_stack: Vec::new(),
130 current_depth: 0,
131 tag_resolver: TagResolver::new(),
132 }
133 }
134
135 fn compose_node(&mut self) -> Result<Option<Value>> {
137 if !self.parser.check_event() {
138 return Ok(None);
139 }
140
141 let Some(event) = self.parser.get_event()? else {
142 return Ok(None);
143 };
144
145 self.position = event.position;
146
147 match event.event_type {
148 EventType::StreamStart | EventType::StreamEnd => {
149 self.compose_node()
151 }
152
153 EventType::DocumentStart { .. } => {
154 self.compose_node()
156 }
157
158 EventType::DocumentEnd { .. } => {
159 Ok(None)
161 }
162
163 EventType::Scalar {
164 value,
165 anchor,
166 tag,
167 style,
168 ..
169 } => {
170 let scalar_value = if let Some(tag_str) = tag {
171 self.compose_tagged_scalar(value, tag_str)?
173 } else {
174 self.compose_scalar(value, style)?
176 };
177
178 if let Some(anchor_name) = anchor {
180 self.resource_tracker.add_anchor(&self.limits)?;
181 self.anchors.insert(anchor_name, scalar_value.clone());
182 }
183
184 Ok(Some(scalar_value))
185 }
186
187 EventType::SequenceStart { anchor, .. } => {
188 let sequence = self.compose_sequence()?;
189
190 if let Some(anchor_name) = anchor {
192 if let Some(ref seq) = sequence {
193 self.resource_tracker.add_anchor(&self.limits)?;
194 self.anchors.insert(anchor_name, seq.clone());
195 }
196 }
197
198 Ok(sequence)
199 }
200
201 EventType::MappingStart { anchor, .. } => {
202 let mapping = self.compose_mapping()?;
203
204 if let Some(anchor_name) = anchor {
206 if let Some(ref map) = mapping {
207 self.resource_tracker.add_anchor(&self.limits)?;
208 self.anchors.insert(anchor_name, map.clone());
209 }
210 }
211
212 Ok(mapping)
213 }
214
215 EventType::SequenceEnd | EventType::MappingEnd => {
216 Ok(None)
221 }
222
223 EventType::Alias { anchor } => {
224 if self.alias_expansion_stack.contains(&anchor) {
226 return Err(Error::construction(
227 event.position,
228 format!("Cyclic alias reference detected: '{anchor}'"),
229 ));
230 }
231
232 if self.alias_expansion_stack.len() >= self.limits.max_alias_depth {
234 return Err(Error::construction(
235 event.position,
236 format!(
237 "Maximum alias expansion depth {} exceeded",
238 self.limits.max_alias_depth
239 ),
240 ));
241 }
242
243 self.resource_tracker.enter_alias(&self.limits)?;
245 self.alias_expansion_stack.push(anchor.clone());
246
247 let result = match self.anchors.get(&anchor) {
249 Some(value) => {
250 let structure_depth = calculate_structure_depth(value);
252 if structure_depth > self.limits.max_alias_depth {
253 return Err(Error::construction(
254 event.position,
255 format!(
256 "Alias '{}' creates structure with depth {} exceeding max_alias_depth {}",
257 anchor, structure_depth, self.limits.max_alias_depth
258 ),
259 ));
260 }
261
262 self.resource_tracker
264 .add_complexity(&self.limits, calculate_value_complexity(value)?)?;
265 Ok(Some(value.clone()))
266 }
267 None => Err(Error::construction(
268 event.position,
269 format!("Unknown anchor '{anchor}'"),
270 )),
271 };
272
273 self.alias_expansion_stack.pop();
275 self.resource_tracker.exit_alias();
276
277 result
278 }
279 }
280 }
281
282 fn compose_scalar(&self, value: String, style: crate::parser::ScalarStyle) -> Result<Value> {
284 match style {
286 crate::parser::ScalarStyle::SingleQuoted | crate::parser::ScalarStyle::DoubleQuoted => {
287 return Ok(Value::String(value));
288 }
289 _ => {
290 }
292 }
293
294 if value.is_empty() {
296 return Ok(Value::String(value));
297 }
298
299 if let Ok(int_value) = value.parse::<i64>() {
301 return Ok(Value::Int(int_value));
302 }
303
304 if let Ok(float_value) = value.parse::<f64>() {
306 return Ok(Value::Float(float_value));
307 }
308
309 match value.to_lowercase().as_str() {
311 "true" | "yes" | "on" => return Ok(Value::Bool(true)),
312 "false" | "no" | "off" => return Ok(Value::Bool(false)),
313 "null" | "~" => return Ok(Value::Null),
314 _ => {}
315 }
316
317 Ok(Value::String(value))
319 }
320
321 fn compose_tagged_scalar(&mut self, value: String, tag_str: String) -> Result<Value> {
323 let tag = self.tag_resolver.resolve(&tag_str)?;
325
326 self.tag_resolver.apply_tag(&tag, &value)
328 }
329
330 fn compose_sequence(&mut self) -> Result<Option<Value>> {
332 self.current_depth += 1;
334 self.resource_tracker
335 .check_depth(&self.limits, self.current_depth)?;
336
337 let mut sequence = Vec::new();
338
339 while self.parser.check_event() {
340 if let Ok(Some(event)) = self.parser.peek_event() {
342 if matches!(event.event_type, EventType::SequenceEnd) {
343 self.parser.get_event()?;
345 break;
346 } else if matches!(
347 event.event_type,
348 EventType::DocumentEnd { .. }
349 | EventType::DocumentStart { .. }
350 | EventType::StreamEnd
351 ) {
352 break;
354 }
355 }
356
357 if let Some(node) = self.compose_node()? {
359 self.resource_tracker.add_collection_item(&self.limits)?;
360 self.resource_tracker.add_complexity(&self.limits, 1)?;
361 sequence.push(node);
362 } else {
363 break;
365 }
366 }
367
368 self.current_depth -= 1;
369 Ok(Some(Value::Sequence(sequence)))
370 }
371
372 fn compose_mapping(&mut self) -> Result<Option<Value>> {
374 self.current_depth += 1;
376 self.resource_tracker
377 .check_depth(&self.limits, self.current_depth)?;
378
379 let mut mapping = IndexMap::new();
380
381 while self.parser.check_event() {
382 if let Ok(Some(event)) = self.parser.peek_event() {
384 if matches!(event.event_type, EventType::MappingEnd) {
385 self.parser.get_event()?;
387 break;
388 } else if matches!(
389 event.event_type,
390 EventType::DocumentEnd { .. }
391 | EventType::DocumentStart { .. }
392 | EventType::StreamEnd
393 ) {
394 break;
396 }
397 }
398
399 let Some(key) = self.compose_node()? else {
401 break;
402 };
403
404 let value = self.compose_node()?.unwrap_or(Value::Null);
406
407 if let Value::String(key_str) = &key {
409 if key_str == "<<" {
410 self.process_merge_key(&mut mapping, &value)?;
412 continue;
413 }
414 }
415
416 self.resource_tracker.add_collection_item(&self.limits)?;
417 self.resource_tracker.add_complexity(&self.limits, 2)?; mapping.insert(key, value);
419 }
420
421 self.current_depth -= 1;
422 Ok(Some(Value::Mapping(mapping)))
423 }
424
425 fn process_merge_key(
428 &self,
429 mapping: &mut IndexMap<Value, Value>,
430 merge_value: &Value,
431 ) -> Result<()> {
432 match merge_value {
433 Value::Mapping(source_map) => {
435 for (key, value) in source_map {
436 mapping.entry(key.clone()).or_insert_with(|| value.clone());
438 }
439 }
440
441 Value::Sequence(sources) => {
443 for source in sources {
444 if let Value::Mapping(source_map) = source {
445 for (key, value) in source_map {
446 mapping.entry(key.clone()).or_insert_with(|| value.clone());
448 }
449 } else {
450 return Err(Error::construction(
451 self.position,
452 "Merge key sequence can only contain mappings",
453 ));
454 }
455 }
456 }
457
458 _ => {
459 return Err(Error::construction(
460 self.position,
461 "Merge key value must be a mapping or sequence of mappings",
462 ));
463 }
464 }
465
466 Ok(())
467 }
468}
469
470impl Composer for BasicComposer {
471 fn check_document(&self) -> bool {
472 if let Ok(Some(event)) = self.parser.peek_event() {
474 !matches!(event.event_type, EventType::StreamEnd)
475 } else {
476 false
477 }
478 }
479
480 fn compose_document(&mut self) -> Result<Option<Value>> {
481 if let Some(error) = self.parser.take_scanning_error() {
483 return Err(error);
484 }
485
486 while let Ok(Some(event)) = self.parser.peek_event() {
488 if let EventType::DocumentStart { tags, .. } = &event.event_type {
489 self.tag_resolver.clear_directives();
491
492 for (handle, prefix) in tags {
494 self.tag_resolver
495 .add_directive(handle.clone(), prefix.clone());
496 }
497
498 self.parser.get_event()?; } else if matches!(event.event_type, EventType::DocumentStart { .. }) {
500 self.parser.get_event()?; } else {
502 break;
503 }
504 }
505
506 let document = self.compose_node()?;
508
509 while let Ok(Some(event)) = self.parser.peek_event() {
511 if matches!(event.event_type, EventType::DocumentEnd { .. }) {
512 self.parser.get_event()?; } else {
514 break;
515 }
516 }
517
518 Ok(document)
519 }
520
521 fn position(&self) -> Position {
522 self.position
523 }
524
525 fn reset(&mut self) {
526 self.position = Position::new();
527 self.anchors.clear();
528 self.resource_tracker.reset();
529 self.alias_expansion_stack.clear();
530 self.current_depth = 0;
531 self.tag_resolver = TagResolver::new();
532 }
533}
534
535impl Default for BasicComposer {
536 fn default() -> Self {
537 Self::new(String::new())
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544 use indexmap::IndexMap;
545
546 #[test]
547 fn test_check_document() {
548 let mut composer = BasicComposer::new_eager("42".to_string());
549 assert!(composer.check_document());
550
551 let _document = composer.compose_document().unwrap();
552 }
554
555 #[test]
556 fn test_scalar_composition() {
557 let mut composer = BasicComposer::new_eager("42".to_string());
558 let document = composer.compose_document().unwrap().unwrap();
559 assert_eq!(document, Value::Int(42));
560 }
561
562 #[test]
563 fn test_boolean_composition() {
564 let mut composer = BasicComposer::new_eager("true".to_string());
565 let document = composer.compose_document().unwrap().unwrap();
566 assert_eq!(document, Value::Bool(true));
567 }
568
569 #[test]
570 fn test_null_composition() {
571 let mut composer = BasicComposer::new_eager("~".to_string());
572 let document = composer.compose_document().unwrap().unwrap();
573 assert_eq!(document, Value::Null);
574 }
575
576 #[test]
577 fn test_string_composition() {
578 let mut composer = BasicComposer::new_eager("hello world".to_string());
579 let document = composer.compose_document().unwrap().unwrap();
580 assert_eq!(document, Value::String("hello world".to_string()));
581 }
582
583 #[test]
584 fn test_flow_sequence_composition() {
585 let mut composer = BasicComposer::new_eager("[1, 2, 3]".to_string());
586 let document = composer.compose_document().unwrap().unwrap();
587
588 let expected = Value::Sequence(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
589 assert_eq!(document, expected);
590 }
591
592 #[test]
593 fn test_flow_mapping_composition() {
594 let mut composer = BasicComposer::new_eager("{'key': 'value', 'number': 42}".to_string());
595 let document = composer.compose_document().unwrap().unwrap();
596
597 let mut expected_map = IndexMap::new();
598 expected_map.insert(
599 Value::String("key".to_string()),
600 Value::String("value".to_string()),
601 );
602 expected_map.insert(Value::String("number".to_string()), Value::Int(42));
603 let expected = Value::Mapping(expected_map);
604
605 assert_eq!(document, expected);
606 }
607
608 #[test]
609 fn test_nested_composition() {
610 let yaml_content = "{'users': [{'name': 'Alice', 'age': 30}]}";
611 let mut composer = BasicComposer::new_eager(yaml_content.to_string());
612 let document = composer.compose_document().unwrap().unwrap();
613
614 let mut user = IndexMap::new();
616 user.insert(
617 Value::String("name".to_string()),
618 Value::String("Alice".to_string()),
619 );
620 user.insert(Value::String("age".to_string()), Value::Int(30));
621
622 let users = Value::Sequence(vec![Value::Mapping(user)]);
623
624 let mut expected = IndexMap::new();
625 expected.insert(Value::String("users".to_string()), users);
626
627 assert_eq!(document, Value::Mapping(expected));
628 }
629
630 #[test]
631 fn test_multiple_types() {
632 let yaml_content = "[42, 'hello', true, null]";
633 let mut composer = BasicComposer::new_eager(yaml_content.to_string());
634 let document = composer.compose_document().unwrap().unwrap();
635
636 let expected = Value::Sequence(vec![
637 Value::Int(42),
638 Value::String("hello".to_string()),
639 Value::Bool(true),
640 Value::Null,
641 ]);
642
643 assert_eq!(document, expected);
644 }
645
646 #[test]
647 fn test_merge_keys_simple() {
648 let yaml_content = r"
649base: &base
650 key: value
651 timeout: 30
652
653test:
654 <<: *base
655 environment: prod
656";
657 let mut composer = BasicComposer::new_eager(yaml_content.to_string());
658 let document = composer.compose_document().unwrap().unwrap();
659
660 if let Value::Mapping(ref map) = document {
661 assert!(map.contains_key(&Value::String("base".to_string())));
663
664 if let Some(Value::Mapping(test_map)) = map.get(&Value::String("test".to_string())) {
666 assert!(test_map.contains_key(&Value::String("key".to_string())));
667 assert!(test_map.contains_key(&Value::String("timeout".to_string())));
668 assert!(test_map.contains_key(&Value::String("environment".to_string())));
669
670 assert_eq!(
672 test_map.get(&Value::String("key".to_string())),
673 Some(&Value::String("value".to_string()))
674 );
675 assert_eq!(
676 test_map.get(&Value::String("timeout".to_string())),
677 Some(&Value::Int(30))
678 );
679 assert_eq!(
680 test_map.get(&Value::String("environment".to_string())),
681 Some(&Value::String("prod".to_string()))
682 );
683 } else {
684 panic!("test mapping not found or not a mapping");
685 }
686 } else {
687 panic!("Document should be a mapping, got: {:?}", document);
688 }
689 }
690
691 #[test]
692 fn test_debug_alias_tokens() {
693 let yaml_content = r"
694base: &base
695 key: value
696
697ref: *base
698";
699
700 let mut scanner = crate::BasicScanner::new_eager(yaml_content.to_string());
701
702 println!("Scanning tokens for alias test:");
703 let mut token_count = 0;
704
705 while scanner.check_token() {
706 if let Ok(Some(token)) = scanner.get_token() {
707 token_count += 1;
708 println!(
709 "{}: {:?} at {:?}-{:?}",
710 token_count, token.token_type, token.start_position, token.end_position
711 );
712 } else {
713 println!("No more tokens");
714 break;
715 }
716 }
717
718 println!("Total tokens: {}", token_count);
719 }
720
721 #[test]
722 fn test_debug_alias_events() {
723 let yaml_content = r"
724base: &base
725 key: value
726
727ref: *base
728";
729
730 let mut parser = BasicParser::new_eager(yaml_content.to_string());
731
732 println!("Parsing events for alias test:");
733 let mut event_count = 0;
734
735 while parser.check_event() {
736 if let Ok(Some(event)) = parser.get_event() {
737 event_count += 1;
738 println!(
739 "{}: {:?} at {:?}",
740 event_count, event.event_type, event.position
741 );
742 } else {
743 println!("No more events");
744 break;
745 }
746 }
747
748 println!("Total events: {}", event_count);
749 }
750
751 #[test]
752 fn test_simple_scalar_alias_resolution() {
753 let yaml_content = r"
755base: &base 'hello world'
756ref: *base
757";
758 let mut composer = BasicComposer::new_eager(yaml_content.to_string());
759 let document = composer.compose_document().unwrap().unwrap();
760
761 println!("Simple alias document: {:?}", document);
762
763 if let Value::Mapping(ref map) = document {
764 println!("Mapping keys: {:?}", map.keys().collect::<Vec<_>>());
765
766 let base_value = map
767 .get(&Value::String("base".to_string()))
768 .expect("base should exist");
769 let ref_value = map
770 .get(&Value::String("ref".to_string()))
771 .expect("ref should exist");
772
773 println!("base_value: {:?}", base_value);
774 println!("ref_value: {:?}", ref_value);
775
776 assert_eq!(base_value, ref_value);
777 } else {
778 panic!("Document should be a mapping, got: {:?}", document);
779 }
780 }
781
782 #[test]
783 fn test_basic_alias_resolution() {
784 let yaml_content = r"
785base: &base
786 key: value
787
788ref: *base
789";
790 let mut composer = BasicComposer::new_eager(yaml_content.to_string());
791 let document = composer.compose_document().unwrap().unwrap();
792
793 println!("Composed document: {:?}", document);
794
795 if let Value::Mapping(ref map) = document {
796 println!("Mapping keys: {:?}", map.keys().collect::<Vec<_>>());
797
798 let base_value = map
800 .get(&Value::String("base".to_string()))
801 .expect("base should exist");
802 let ref_value = map
803 .get(&Value::String("ref".to_string()))
804 .expect("ref should exist");
805
806 println!("base_value: {:?}", base_value);
807 println!("ref_value: {:?}", ref_value);
808
809 assert_eq!(
811 base_value, ref_value,
812 "Alias should resolve to the same value as the anchor"
813 );
814
815 if let Value::Mapping(nested) = base_value {
817 assert_eq!(
818 nested.get(&Value::String("key".to_string())),
819 Some(&Value::String("value".to_string()))
820 );
821 } else {
822 panic!("base value should be a nested mapping");
823 }
824
825 println!("✅ Alias resolution working correctly!");
826 } else {
827 panic!("Document should be a mapping, got: {:?}", document);
828 }
829 }
830}