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