use crate::yaml::{Document, Mapping, Scalar, Sequence, YamlFile};
use rowan::ast::AstNode;
pub trait YamlVisitor {
fn visit_yaml(&mut self, yaml: &YamlFile) {
for doc in yaml.documents() {
self.visit_document(&doc);
}
}
fn visit_document(&mut self, document: &Document) {
if let Some(mapping) = document.as_mapping() {
self.visit_mapping(&mapping);
} else if let Some(sequence) = document.as_sequence() {
self.visit_sequence(&sequence);
} else if let Some(scalar) = document.as_scalar() {
self.visit_scalar(&scalar);
}
}
fn visit_scalar(&mut self, _scalar: &Scalar) {}
fn visit_mapping(&mut self, mapping: &Mapping) {
self.walk_mapping(mapping);
}
fn visit_sequence(&mut self, sequence: &Sequence) {
self.walk_sequence(sequence);
}
fn walk_mapping(&mut self, mapping: &Mapping) {
use crate::yaml::{extract_mapping, extract_scalar, extract_sequence};
for (key_node, value_node) in mapping.pairs() {
if let Some(scalar) = extract_scalar(&key_node) {
self.visit_scalar(&scalar);
} else if let Some(sequence) = extract_sequence(&key_node) {
self.visit_sequence(&sequence);
} else if let Some(mapping) = extract_mapping(&key_node) {
self.visit_mapping(&mapping);
}
if let Some(scalar) = extract_scalar(&value_node) {
self.visit_scalar(&scalar);
} else if let Some(nested_mapping) = extract_mapping(&value_node) {
self.visit_mapping(&nested_mapping);
} else if let Some(nested_sequence) = extract_sequence(&value_node) {
self.visit_sequence(&nested_sequence);
}
}
}
fn walk_sequence(&mut self, sequence: &Sequence) {
for item in sequence.items() {
if let Some(scalar) = Scalar::cast(item.clone()) {
self.visit_scalar(&scalar);
} else if let Some(nested_mapping) = Mapping::cast(item.clone()) {
self.visit_mapping(&nested_mapping);
} else if let Some(nested_sequence) = Sequence::cast(item.clone()) {
self.visit_sequence(&nested_sequence);
}
}
}
}
pub trait YamlAccept {
fn accept<V: YamlVisitor>(&self, visitor: &mut V);
}
impl YamlAccept for YamlFile {
fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
visitor.visit_yaml(self);
}
}
impl YamlAccept for Document {
fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
visitor.visit_document(self);
}
}
impl YamlAccept for Scalar {
fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
visitor.visit_scalar(self);
}
}
impl YamlAccept for Mapping {
fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
visitor.visit_mapping(self);
}
}
impl YamlAccept for Sequence {
fn accept<V: YamlVisitor>(&self, visitor: &mut V) {
visitor.visit_sequence(self);
}
}
pub struct ScalarCollector {
pub scalars: Vec<String>,
}
impl ScalarCollector {
pub fn new() -> Self {
Self {
scalars: Vec::new(),
}
}
}
impl Default for ScalarCollector {
fn default() -> Self {
Self::new()
}
}
impl YamlVisitor for ScalarCollector {
fn visit_scalar(&mut self, scalar: &Scalar) {
self.scalars.push(scalar.to_string());
}
}
pub struct NodeCounter {
pub document_count: usize,
pub scalar_count: usize,
pub mapping_count: usize,
pub sequence_count: usize,
}
impl NodeCounter {
pub fn new() -> Self {
Self {
document_count: 0,
scalar_count: 0,
mapping_count: 0,
sequence_count: 0,
}
}
pub fn total(&self) -> usize {
self.document_count + self.scalar_count + self.mapping_count + self.sequence_count
}
}
impl Default for NodeCounter {
fn default() -> Self {
Self::new()
}
}
impl YamlVisitor for NodeCounter {
fn visit_document(&mut self, document: &Document) {
self.document_count += 1;
if let Some(mapping) = document.as_mapping() {
self.visit_mapping(&mapping);
} else if let Some(sequence) = document.as_sequence() {
self.visit_sequence(&sequence);
} else if let Some(scalar) = document.as_scalar() {
self.visit_scalar(&scalar);
}
}
fn visit_scalar(&mut self, _scalar: &Scalar) {
self.scalar_count += 1;
}
fn visit_mapping(&mut self, mapping: &Mapping) {
self.mapping_count += 1;
self.walk_mapping(mapping);
}
fn visit_sequence(&mut self, sequence: &Sequence) {
self.sequence_count += 1;
self.walk_sequence(sequence);
}
}
pub struct ScalarTransformer<F>
where
F: FnMut(&str) -> String,
{
transform: F,
transformed: Vec<(String, String)>, }
impl<F> ScalarTransformer<F>
where
F: FnMut(&str) -> String,
{
pub fn new(transform: F) -> Self {
Self {
transform,
transformed: Vec::new(),
}
}
pub fn results(&self) -> &[(String, String)] {
&self.transformed
}
}
impl<F> YamlVisitor for ScalarTransformer<F>
where
F: FnMut(&str) -> String,
{
fn visit_scalar(&mut self, scalar: &Scalar) {
let original = scalar.to_string();
let transformed = (self.transform)(&original);
self.transformed.push((original, transformed));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::YamlFile;
#[test]
fn test_scalar_collector() {
let yaml_text = r#"
name: John Doe
age: 30
address:
street: 123 Main St
city: New York
country: USA
hobbies:
- reading
- coding
- hiking
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut collector = ScalarCollector::new();
yaml.accept(&mut collector);
assert_eq!(
collector.scalars,
vec![
"name",
"John Doe",
"age",
"30",
"address",
"street",
"123 Main St",
"city",
"New York",
"country",
"USA",
"hobbies",
"reading",
"coding",
"hiking",
]
);
}
#[test]
fn test_node_counter() {
let yaml_text = r#"
name: John Doe
age: 30
address:
street: 123 Main St
city: New York
country: USA
hobbies:
- reading
- coding
- hiking
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut counter = NodeCounter::new();
yaml.accept(&mut counter);
assert_eq!(counter.document_count, 1);
assert_eq!(counter.scalar_count, 15);
assert_eq!(counter.mapping_count, 2); assert_eq!(counter.sequence_count, 1);
assert_eq!(
counter.total(),
counter.document_count
+ counter.scalar_count
+ counter.mapping_count
+ counter.sequence_count
);
}
#[test]
fn test_scalar_transformer() {
let yaml_text = r#"
name: john
city: new york
country: usa
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut transformer = ScalarTransformer::new(|s: &str| s.to_uppercase());
yaml.accept(&mut transformer);
let results = transformer.results();
assert_eq!(
results,
&[
("name".to_string(), "NAME".to_string()),
("john".to_string(), "JOHN".to_string()),
("city".to_string(), "CITY".to_string()),
("new york".to_string(), "NEW YORK".to_string()),
("country".to_string(), "COUNTRY".to_string()),
("usa".to_string(), "USA".to_string()),
]
);
}
#[test]
fn test_visitor_on_sequence() {
let yaml_text = r#"
- item1
- item2
- nested:
- subitem1
- subitem2
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut collector = ScalarCollector::new();
yaml.accept(&mut collector);
assert_eq!(
collector.scalars,
vec!["item1", "item2", "nested", "subitem1", "subitem2"]
);
}
#[test]
fn test_visitor_on_empty_document() {
let yaml_text = "";
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut counter = NodeCounter::new();
yaml.accept(&mut counter);
assert_eq!(counter.total(), 0);
}
#[test]
fn test_custom_visitor() {
struct KeyCounter {
key_count: usize,
}
impl YamlVisitor for KeyCounter {
fn visit_scalar(&mut self, _scalar: &crate::Scalar) {
}
fn visit_mapping(&mut self, mapping: &crate::Mapping) {
for (_key, value) in mapping.iter() {
self.key_count += 1;
if let Some(nested_mapping) = value.as_mapping() {
nested_mapping.accept(self);
} else if let Some(nested_sequence) = value.as_sequence() {
nested_sequence.accept(self);
}
}
}
fn visit_sequence(&mut self, sequence: &crate::Sequence) {
for value in sequence.values() {
if let Some(nested_mapping) = value.as_mapping() {
nested_mapping.accept(self);
} else if let Some(nested_sequence) = value.as_sequence() {
nested_sequence.accept(self);
}
}
}
}
let yaml_text = r#"
name: John
age: 30
address:
street: 123 Main St
city: New York
metadata:
created: 2024-01-01
updated: 2024-01-02
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut key_counter = KeyCounter { key_count: 0 };
yaml.accept(&mut key_counter);
assert_eq!(key_counter.key_count, 8);
}
#[test]
fn test_visitor_with_multiple_documents() {
let yaml_text = r#"
---
doc1: value1
---
doc2: value2
---
doc3: value3
"#;
let parsed = YamlFile::parse(yaml_text);
let yaml = parsed.tree();
let mut counter = NodeCounter::new();
yaml.accept(&mut counter);
assert_eq!(counter.document_count, 3);
assert!(counter.scalar_count >= 6); }
}