icydb_schema/
visit.rs

1use crate::{node::VisitableNode, prelude::*};
2
3///
4/// Event
5/// fine to redeclare this every time we use the visitor pattern
6/// makes no sense to rely on such a simple dependency
7///
8
9#[derive(Debug)]
10pub enum Event {
11    Enter,
12    Exit,
13}
14
15///
16/// Visitor
17///
18
19pub trait Visitor {
20    // visit
21    fn visit<V: VisitableNode + ?Sized>(&mut self, _: &V, _: Event) {}
22
23    // key
24    fn push(&mut self, _: &str) {}
25    fn pop(&mut self) {}
26}
27
28///
29/// ValidateVisitor
30///
31
32#[derive(Debug, Default)]
33pub struct ValidateVisitor {
34    pub errors: ErrorTree,
35    pub path: Vec<String>,
36    pub node_count: usize,
37}
38
39impl ValidateVisitor {
40    #[must_use]
41    pub fn new() -> Self {
42        Self {
43            errors: ErrorTree::new(),
44            ..Default::default()
45        }
46    }
47
48    #[must_use]
49    pub const fn node_count(&self) -> usize {
50        self.node_count
51    }
52}
53
54impl ValidateVisitor {
55    fn current_route(&self) -> String {
56        self.path
57            .iter()
58            .filter(|s| !s.is_empty())
59            .cloned()
60            .collect::<Vec<_>>()
61            .join(".")
62    }
63}
64
65impl Visitor for ValidateVisitor {
66    fn visit<T: VisitableNode + ?Sized>(&mut self, node: &T, event: Event) {
67        match event {
68            Event::Enter => {
69                self.node_count += 1;
70
71                match node.validate() {
72                    Ok(()) => {}
73                    Err(errs) => {
74                        if !errs.is_empty() {
75                            let route = self.current_route();
76
77                            if route.is_empty() {
78                                // At the current level, merge directly.
79                                self.errors.merge(errs);
80                            } else {
81                                // Add to a child entry under the computed route.
82                                self.errors.children.entry(route).or_default().merge(errs);
83                            }
84                        }
85                    }
86                }
87            }
88            Event::Exit => {}
89        }
90    }
91
92    fn push(&mut self, s: &str) {
93        self.path.push(s.to_string());
94    }
95
96    fn pop(&mut self) {
97        self.path.pop();
98    }
99}