shape/
visitor.rs

1use indexmap::IndexMap;
2
3use crate::case_enum::all::All;
4use crate::case_enum::one::One;
5use crate::name::Name;
6use crate::name::WeakScope;
7use crate::Shape;
8use crate::ShapeCase;
9
10#[expect(unused_variables, clippy::missing_errors_doc)]
11pub trait ShapeVisitor<Output = ()> {
12    type Error: std::error::Error;
13    type Output;
14
15    fn default(&mut self, shape: &Shape) -> Result<Self::Output, Self::Error>;
16
17    fn visit_bool(
18        &mut self,
19        shape: &Shape,
20        value: &Option<bool>,
21    ) -> Result<Self::Output, Self::Error> {
22        self.default(shape)
23    }
24
25    fn visit_string(
26        &mut self,
27        shape: &Shape,
28        value: &Option<String>,
29    ) -> Result<Self::Output, Self::Error> {
30        self.default(shape)
31    }
32
33    fn visit_int(
34        &mut self,
35        shape: &Shape,
36        value: &Option<i64>,
37    ) -> Result<Self::Output, Self::Error> {
38        self.default(shape)
39    }
40
41    fn visit_float(&mut self, shape: &Shape) -> Result<Self::Output, Self::Error> {
42        self.default(shape)
43    }
44
45    fn visit_null(&mut self, shape: &Shape) -> Result<Self::Output, Self::Error> {
46        self.default(shape)
47    }
48
49    fn visit_array(
50        &mut self,
51        shape: &Shape,
52        prefix: &[Shape],
53        tail: &Shape,
54    ) -> Result<Self::Output, Self::Error> {
55        self.default(shape)
56    }
57
58    fn visit_object(
59        &mut self,
60        shape: &Shape,
61        fields: &IndexMap<String, Shape>,
62        rest: &Shape,
63    ) -> Result<Self::Output, Self::Error> {
64        self.default(shape)
65    }
66
67    fn visit_one(&mut self, shape: &Shape, one: &One) -> Result<Self::Output, Self::Error> {
68        self.default(shape)
69    }
70
71    fn visit_name(
72        &mut self,
73        shape: &Shape,
74        name: &Name,
75        weak: &WeakScope,
76    ) -> Result<Self::Output, Self::Error> {
77        self.default(shape)
78    }
79
80    fn visit_unknown(&mut self, shape: &Shape) -> Result<Self::Output, Self::Error> {
81        self.default(shape)
82    }
83
84    fn visit_none(&mut self, shape: &Shape) -> Result<Self::Output, Self::Error> {
85        self.default(shape)
86    }
87
88    fn visit_all(&mut self, shape: &Shape, all: &All) -> Result<Self::Output, Self::Error> {
89        self.default(shape)
90    }
91}
92
93impl Shape {
94    #[expect(clippy::missing_errors_doc)]
95    pub fn visit_shape<V: ShapeVisitor>(&self, visitor: &mut V) -> Result<V::Output, V::Error> {
96        match self.case() {
97            ShapeCase::Bool(b) => visitor.visit_bool(self, b),
98            ShapeCase::String(s) => visitor.visit_string(self, s),
99            ShapeCase::Int(i) => visitor.visit_int(self, i),
100            ShapeCase::Float => visitor.visit_float(self),
101            ShapeCase::Null => visitor.visit_null(self),
102            ShapeCase::Array { prefix, tail } => visitor.visit_array(self, prefix, tail),
103            ShapeCase::Object { fields, rest } => visitor.visit_object(self, fields, rest),
104            ShapeCase::One(one) => visitor.visit_one(self, one),
105            ShapeCase::All(all) => visitor.visit_all(self, all),
106            // Visitor implementations may call weak.upgrade(name) to get the
107            // resolved target of the name, if any. It's tempting to perform
108            // that resolution here unconditionally, but then the visitor could
109            // fall into an infinitely deep cycle, so we have to let the
110            // implementation of visit_name decide whether to proceed further.
111            ShapeCase::Name(name, weak) => visitor.visit_name(self, name, weak),
112            ShapeCase::Unknown => visitor.visit_unknown(self),
113            ShapeCase::None => visitor.visit_none(self),
114        }
115    }
116}