1use crate::yaml::{Document, Mapping, Scalar, Sequence, TaggedNode, YamlFile};
39
40pub trait YamlVisitor {
42 fn visit_yaml(&mut self, yaml: &YamlFile) {
44 for doc in yaml.documents() {
46 self.visit_document(&doc);
47 }
48 }
49
50 fn visit_document(&mut self, document: &Document) {
52 if let Some(mapping) = document.as_mapping() {
54 self.visit_mapping(&mapping);
55 } else if let Some(sequence) = document.as_sequence() {
56 self.visit_sequence(&sequence);
57 } else if let Some(scalar) = document.as_scalar() {
58 self.visit_scalar(&scalar);
59 }
60 }
61
62 fn visit_scalar(&mut self, _scalar: &Scalar) {}
64
65 fn visit_mapping(&mut self, mapping: &Mapping) {
71 self.walk_mapping(mapping);
72 }
73
74 fn visit_sequence(&mut self, sequence: &Sequence) {
79 self.walk_sequence(sequence);
80 }
81
82 fn visit_tagged_node(&mut self, tagged: &TaggedNode) {
88 self.walk_tagged_node(tagged);
89 }
90
91 fn walk_tagged_node(&mut self, tagged: &TaggedNode) {
94 if let Some(mapping) = tagged.as_mapping() {
95 self.visit_mapping(&mapping);
96 } else if let Some(sequence) = tagged.as_sequence() {
97 self.visit_sequence(&sequence);
98 } else if let Some(scalar) = tagged.value() {
99 self.visit_scalar(&scalar);
100 }
101 }
102
103 fn walk_mapping(&mut self, mapping: &Mapping) {
109 use crate::yaml::{extract_mapping, extract_scalar, extract_sequence, extract_tagged_node};
110
111 for (key_node, value_node) in mapping.pairs() {
112 if let Some(scalar) = extract_scalar(&key_node) {
114 self.visit_scalar(&scalar);
115 } else if let Some(tagged) = extract_tagged_node(&key_node) {
116 self.visit_tagged_node(&tagged);
117 } else if let Some(sequence) = extract_sequence(&key_node) {
118 self.visit_sequence(&sequence);
119 } else if let Some(mapping) = extract_mapping(&key_node) {
120 self.visit_mapping(&mapping);
121 }
122
123 if let Some(scalar) = extract_scalar(&value_node) {
125 self.visit_scalar(&scalar);
126 } else if let Some(tagged) = extract_tagged_node(&value_node) {
127 self.visit_tagged_node(&tagged);
128 } else if let Some(nested_mapping) = extract_mapping(&value_node) {
129 self.visit_mapping(&nested_mapping);
130 } else if let Some(nested_sequence) = extract_sequence(&value_node) {
131 self.visit_sequence(&nested_sequence);
132 }
133 }
134 }
135
136 fn walk_sequence(&mut self, sequence: &Sequence) {
142 use crate::yaml::{extract_mapping, extract_scalar, extract_sequence, extract_tagged_node};
143
144 for item in sequence.items() {
145 if let Some(scalar) = extract_scalar(&item) {
146 self.visit_scalar(&scalar);
147 } else if let Some(tagged) = extract_tagged_node(&item) {
148 self.visit_tagged_node(&tagged);
149 } else if let Some(nested_mapping) = extract_mapping(&item) {
150 self.visit_mapping(&nested_mapping);
151 } else if let Some(nested_sequence) = extract_sequence(&item) {
152 self.visit_sequence(&nested_sequence);
153 }
154 }
155 }
156}
157
158pub trait YamlAccept {
160 fn accept<V: YamlVisitor>(&self, visitor: &mut V);
162}
163
164impl YamlAccept for YamlFile {
165 fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
166 visitor.visit_yaml(self);
167 }
168}
169
170impl YamlAccept for Document {
171 fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
172 visitor.visit_document(self);
173 }
174}
175
176impl YamlAccept for Scalar {
177 fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
178 visitor.visit_scalar(self);
179 }
180}
181
182impl YamlAccept for Mapping {
183 fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
184 visitor.visit_mapping(self);
185 }
186}
187
188impl YamlAccept for Sequence {
189 fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
190 visitor.visit_sequence(self);
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct ScalarCollector {
197 pub scalars: Vec<String>,
199}
200
201impl ScalarCollector {
202 pub fn new() -> Self {
204 Self {
205 scalars: Vec::new(),
206 }
207 }
208}
209
210impl Default for ScalarCollector {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl YamlVisitor for ScalarCollector {
217 fn visit_scalar(&mut self, scalar: &Scalar) {
218 self.scalars.push(scalar.to_string());
219 }
220 }
222
223#[derive(Debug, Clone)]
225pub struct NodeCounter {
226 pub document_count: usize,
228 pub scalar_count: usize,
230 pub mapping_count: usize,
232 pub sequence_count: usize,
234}
235
236impl NodeCounter {
237 pub fn new() -> Self {
239 Self {
240 document_count: 0,
241 scalar_count: 0,
242 mapping_count: 0,
243 sequence_count: 0,
244 }
245 }
246
247 pub fn total(&self) -> usize {
249 self.document_count + self.scalar_count + self.mapping_count + self.sequence_count
250 }
251}
252
253impl Default for NodeCounter {
254 fn default() -> Self {
255 Self::new()
256 }
257}
258
259impl YamlVisitor for NodeCounter {
260 fn visit_document(&mut self, document: &Document) {
261 self.document_count += 1;
262 if let Some(mapping) = document.as_mapping() {
264 self.visit_mapping(&mapping);
265 } else if let Some(sequence) = document.as_sequence() {
266 self.visit_sequence(&sequence);
267 } else if let Some(scalar) = document.as_scalar() {
268 self.visit_scalar(&scalar);
269 }
270 }
271
272 fn visit_scalar(&mut self, _scalar: &Scalar) {
273 self.scalar_count += 1;
274 }
275
276 fn visit_mapping(&mut self, mapping: &Mapping) {
277 self.mapping_count += 1;
278 self.walk_mapping(mapping);
280 }
281
282 fn visit_sequence(&mut self, sequence: &Sequence) {
283 self.sequence_count += 1;
284 self.walk_sequence(sequence);
286 }
287}
288
289pub struct ScalarTransformer<F>
291where
292 F: FnMut(&str) -> String,
293{
294 transform: F,
295 transformed: Vec<(String, String)>, }
297
298impl<F> ScalarTransformer<F>
299where
300 F: FnMut(&str) -> String,
301{
302 pub fn new(transform: F) -> Self {
304 Self {
305 transform,
306 transformed: Vec::new(),
307 }
308 }
309
310 pub fn results(&self) -> &[(String, String)] {
312 &self.transformed
313 }
314}
315
316impl<F> YamlVisitor for ScalarTransformer<F>
317where
318 F: FnMut(&str) -> String,
319{
320 fn visit_scalar(&mut self, scalar: &Scalar) {
321 let original = scalar.to_string();
322 let transformed = (self.transform)(&original);
323 self.transformed.push((original, transformed));
324 }
325
326 }
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::YamlFile;
332
333 struct ScalarNames {
334 seen: Vec<String>,
335 }
336
337 impl YamlVisitor for ScalarNames {
338 fn visit_scalar(&mut self, scalar: &Scalar) {
339 self.seen.push(scalar.as_string());
340 }
341 }
342
343 fn visited_scalars(src: &str) -> Vec<String> {
344 use std::str::FromStr;
345 let doc = Document::from_str(src).unwrap();
346 let mut v = ScalarNames { seen: vec![] };
347 doc.accept(&mut v);
348 v.seen
349 }
350
351 #[test]
352 fn test_walks_into_tagged_nodes() {
353 assert_eq!(visited_scalars("- !!str a\n- b\n"), ["a", "b"]);
357 assert_eq!(visited_scalars("k: !!str v\n"), ["k", "v"]);
358 assert_eq!(visited_scalars("!!str k: v\n"), ["k", "v"]);
359 assert_eq!(visited_scalars("k: !!map\n a: 1\n"), ["k", "a", "1"]);
360 assert_eq!(visited_scalars("- !!seq\n - a\n"), ["a"]);
361 }
362
363 #[test]
364 fn test_visit_tagged_node_can_be_overridden() {
365 struct Tags {
366 tags: Vec<String>,
367 }
368 impl YamlVisitor for Tags {
369 fn visit_tagged_node(&mut self, tagged: &TaggedNode) {
370 self.tags.push(tagged.tag().unwrap_or_default());
371 }
373 }
374 use std::str::FromStr;
375 let doc = Document::from_str("- !!str a\n- !custom b\n").unwrap();
376 let mut v = Tags { tags: vec![] };
377 doc.accept(&mut v);
378 assert_eq!(v.tags, ["!!str", "!custom"]);
379 }
380
381 #[test]
382 fn test_scalar_collector() {
383 let yaml_text = r#"
384name: John Doe
385age: 30
386address:
387 street: 123 Main St
388 city: New York
389 country: USA
390hobbies:
391 - reading
392 - coding
393 - hiking
394"#;
395
396 let parsed = YamlFile::parse(yaml_text);
397 let yaml = parsed.tree();
398
399 let mut collector = ScalarCollector::new();
400 yaml.accept(&mut collector);
401
402 assert_eq!(
404 collector.scalars,
405 vec![
406 "name",
407 "John Doe",
408 "age",
409 "30",
410 "address",
411 "street",
412 "123 Main St",
413 "city",
414 "New York",
415 "country",
416 "USA",
417 "hobbies",
418 "reading",
419 "coding",
420 "hiking",
421 ]
422 );
423 }
424
425 #[test]
426 fn test_node_counter() {
427 let yaml_text = r#"
428name: John Doe
429age: 30
430address:
431 street: 123 Main St
432 city: New York
433 country: USA
434hobbies:
435 - reading
436 - coding
437 - hiking
438"#;
439
440 let parsed = YamlFile::parse(yaml_text);
441 let yaml = parsed.tree();
442
443 let mut counter = NodeCounter::new();
444 yaml.accept(&mut counter);
445
446 assert_eq!(counter.document_count, 1);
448 assert_eq!(counter.scalar_count, 15);
449 assert_eq!(counter.mapping_count, 2); assert_eq!(counter.sequence_count, 1); assert_eq!(
454 counter.total(),
455 counter.document_count
456 + counter.scalar_count
457 + counter.mapping_count
458 + counter.sequence_count
459 );
460 }
461
462 #[test]
463 fn test_scalar_transformer() {
464 let yaml_text = r#"
465name: john
466city: new york
467country: usa
468"#;
469
470 let parsed = YamlFile::parse(yaml_text);
471 let yaml = parsed.tree();
472
473 let mut transformer = ScalarTransformer::new(|s: &str| s.to_uppercase());
475 yaml.accept(&mut transformer);
476
477 let results = transformer.results();
478
479 assert_eq!(
481 results,
482 &[
483 ("name".to_string(), "NAME".to_string()),
484 ("john".to_string(), "JOHN".to_string()),
485 ("city".to_string(), "CITY".to_string()),
486 ("new york".to_string(), "NEW YORK".to_string()),
487 ("country".to_string(), "COUNTRY".to_string()),
488 ("usa".to_string(), "USA".to_string()),
489 ]
490 );
491 }
492
493 #[test]
494 fn test_visitor_on_sequence() {
495 let yaml_text = r#"
496- item1
497- item2
498- nested:
499 - subitem1
500 - subitem2
501"#;
502
503 let parsed = YamlFile::parse(yaml_text);
504 let yaml = parsed.tree();
505
506 let mut collector = ScalarCollector::new();
507 yaml.accept(&mut collector);
508
509 assert_eq!(
511 collector.scalars,
512 vec!["item1", "item2", "nested", "subitem1", "subitem2"]
513 );
514 }
515
516 #[test]
517 fn test_visitor_on_empty_document() {
518 let yaml_text = "";
519
520 let parsed = YamlFile::parse(yaml_text);
521 let yaml = parsed.tree();
522
523 let mut counter = NodeCounter::new();
524 yaml.accept(&mut counter);
525
526 assert_eq!(counter.total(), 0);
528 }
529
530 #[test]
531 fn test_custom_visitor() {
532 struct KeyCounter {
534 key_count: usize,
535 }
536
537 impl YamlVisitor for KeyCounter {
538 fn visit_scalar(&mut self, _scalar: &crate::Scalar) {
539 }
541
542 fn visit_mapping(&mut self, mapping: &crate::Mapping) {
543 for (_key, value) in mapping.iter() {
545 self.key_count += 1;
546 if let Some(nested_mapping) = value.as_mapping() {
548 nested_mapping.accept(self);
549 } else if let Some(nested_sequence) = value.as_sequence() {
550 nested_sequence.accept(self);
551 }
552 }
553 }
554
555 fn visit_sequence(&mut self, sequence: &crate::Sequence) {
556 for value in sequence.values() {
558 if let Some(nested_mapping) = value.as_mapping() {
559 nested_mapping.accept(self);
560 } else if let Some(nested_sequence) = value.as_sequence() {
561 nested_sequence.accept(self);
562 }
563 }
564 }
565 }
566
567 let yaml_text = r#"
568name: John
569age: 30
570address:
571 street: 123 Main St
572 city: New York
573metadata:
574 created: 2024-01-01
575 updated: 2024-01-02
576"#;
577
578 let parsed = YamlFile::parse(yaml_text);
579 let yaml = parsed.tree();
580
581 let mut key_counter = KeyCounter { key_count: 0 };
582 yaml.accept(&mut key_counter);
583
584 assert_eq!(key_counter.key_count, 8);
586 }
587
588 #[test]
589 fn test_visitor_with_multiple_documents() {
590 let yaml_text = r#"
591---
592doc1: value1
593---
594doc2: value2
595---
596doc3: value3
597"#;
598
599 let parsed = YamlFile::parse(yaml_text);
600 let yaml = parsed.tree();
601
602 let mut counter = NodeCounter::new();
603 yaml.accept(&mut counter);
604
605 assert_eq!(counter.document_count, 3);
607 assert!(counter.scalar_count >= 6); }
609}