Skip to main content

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    errors: ErrorTree,
35    path: Vec<String>,
36    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    #[must_use]
54    pub const fn errors(&self) -> &ErrorTree {
55        &self.errors
56    }
57
58    #[must_use]
59    pub fn into_errors(self) -> ErrorTree {
60        self.errors
61    }
62}
63
64impl ValidateVisitor {
65    fn current_route(&self) -> String {
66        self.path
67            .iter()
68            .filter(|s| !s.is_empty())
69            .cloned()
70            .collect::<Vec<_>>()
71            .join(".")
72    }
73}
74
75impl Visitor for ValidateVisitor {
76    fn visit<T: VisitableNode + ?Sized>(&mut self, node: &T, event: Event) {
77        match event {
78            Event::Enter => {
79                self.node_count += 1;
80
81                match node.validate() {
82                    Ok(()) => {}
83                    Err(errs) => {
84                        if !errs.is_empty() {
85                            let route = self.current_route();
86
87                            if route.is_empty() {
88                                // At the current level, merge directly.
89                                self.errors.merge(errs);
90                            } else {
91                                // Add to a child entry under the computed route.
92                                self.errors.merge_for(route, errs);
93                            }
94                        }
95                    }
96                }
97            }
98            Event::Exit => {}
99        }
100    }
101
102    fn push(&mut self, s: &str) {
103        self.path.push(s.to_string());
104    }
105
106    fn pop(&mut self) {
107        self.path.pop();
108    }
109}