1use crate::{ItemKind, TaggedItem};
2
3#[allow(unused_variables)]
71pub trait Visitor<'a, Err> {
72 fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), Err> {
74 Ok(())
75 }
76 fn visit_array_begin(&mut self, array: TaggedItem<'a>, size: Option<u64>) -> Result<bool, Err> {
80 Ok(true)
81 }
82 fn visit_array_index(&mut self, array: TaggedItem<'a>, index: u64) -> Result<bool, Err> {
86 Ok(true)
87 }
88 fn visit_array_end(&mut self, array: TaggedItem<'a>) -> Result<(), Err> {
90 Ok(())
91 }
92 fn visit_dict_begin(&mut self, dict: TaggedItem<'a>, size: Option<u64>) -> Result<bool, Err> {
96 Ok(true)
97 }
98 fn visit_dict_key(
105 &mut self,
106 dict: TaggedItem<'a>,
107 key: TaggedItem<'a>,
108 is_first: bool,
109 ) -> Result<bool, Err> {
110 Ok(true)
111 }
112 fn visit_dict_end(&mut self, dict: TaggedItem<'a>) -> Result<(), Err> {
114 Ok(())
115 }
116}
117
118pub fn visit<'a, 'b, Err, V: Visitor<'b, Err>>(v: &'a mut V, c: TaggedItem<'b>) -> Result<(), Err> {
119 match c.kind() {
120 ItemKind::Array(iter) => {
121 if v.visit_array_begin(c, iter.size())? {
122 for (idx, item) in iter.enumerate() {
123 if v.visit_array_index(c, idx as u64)? {
124 visit(v, item.tagged_item())?;
125 }
126 }
127 }
128
129 v.visit_array_end(c)
130 }
131 ItemKind::Dict(iter) => {
132 if v.visit_dict_begin(c, iter.size())? {
133 let mut is_first = true;
134 for (key, item) in iter {
135 if v.visit_dict_key(c, key.tagged_item(), is_first)? {
136 visit(v, item.tagged_item())?;
137 }
138 is_first = false;
139 }
140 }
141
142 v.visit_dict_end(c)
143 }
144 _ => v.visit_simple(c),
145 }
146}
147
148impl<'a> Visitor<'a, std::fmt::Error> for &mut std::fmt::Formatter<'_> {
150 fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
151 write!(self, "{}", item)?;
152 Ok(())
153 }
154
155 fn visit_array_begin(
156 &mut self,
157 array: TaggedItem<'a>,
158 size: Option<u64>,
159 ) -> Result<bool, std::fmt::Error> {
160 for tag in array.tags() {
161 write!(self, "{}(", tag)?;
162 }
163 write!(self, "[")?;
164 if size.is_none() {
165 write!(self, "_ ")?;
166 }
167 Ok(true)
168 }
169
170 fn visit_array_index(
171 &mut self,
172 _array: TaggedItem<'a>,
173 index: u64,
174 ) -> Result<bool, std::fmt::Error> {
175 if index > 0 {
176 write!(self, ", ")?;
177 }
178 Ok(true)
179 }
180
181 fn visit_array_end(&mut self, array: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
182 write!(self, "]")?;
183 for _ in array.tags() {
184 write!(self, ")")?;
185 }
186 Ok(())
187 }
188
189 fn visit_dict_begin(
190 &mut self,
191 dict: TaggedItem<'a>,
192 size: Option<u64>,
193 ) -> Result<bool, std::fmt::Error> {
194 for tag in dict.tags() {
195 write!(self, "{}(", tag)?;
196 }
197 write!(self, "{{")?;
198 if size.is_none() {
199 write!(self, "_ ")?;
200 }
201 Ok(true)
202 }
203
204 fn visit_dict_key(
205 &mut self,
206 _dict: TaggedItem<'a>,
207 key: TaggedItem<'a>,
208 is_first: bool,
209 ) -> Result<bool, std::fmt::Error> {
210 if !is_first {
211 write!(self, ", ")?;
212 }
213 visit(self, key)?;
214 write!(self, ": ")?;
215 Ok(true)
216 }
217
218 fn visit_dict_end(&mut self, dict: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
219 write!(self, "}}")?;
220 for _ in dict.tags() {
221 write!(self, ")")?;
222 }
223 Ok(())
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use crate::{constants::TAG_CBOR_ITEM, CborBuilder, ItemKind, PathElement, TaggedItem, Writer};
230 use pretty_assertions::assert_eq;
231
232 #[test]
233 fn smoke() {
234 let x = CborBuilder::new().write_array([1, 2], |b| {
235 b.write_bool(true, [3, 4]);
236 b.write_dict([5, 6], |b| {
237 b.with_key("k", |b| b.write_pos(5, [7, 8]));
238 b.with_cbor_key(|b| b.write_neg(42, [9, 10]), |b| b.write_null([11, 12]));
239 });
240 b.write_bytes(
241 CborBuilder::new()
242 .write_array([], |b| {
243 b.write_bool(false, [0]);
244 b.write_undefined([42]);
245 })
246 .as_slice(),
247 [TAG_CBOR_ITEM],
248 );
249 });
250
251 struct Visitor<'b>(&'b mut Vec<String>, bool);
252 impl<'a, 'b> super::Visitor<'a, &'static str> for Visitor<'b> {
253 fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), &'static str> {
254 self.0.push(format!("simple {:?}", item));
255 if self.1 {
256 Err("buh")
257 } else {
258 Ok(())
259 }
260 }
261
262 fn visit_array_begin(
263 &mut self,
264 array: TaggedItem<'a>,
265 size: Option<u64>,
266 ) -> Result<bool, &'static str> {
267 self.0.push(format!("array_begin {:?} {:?}", array, size));
268 Ok(true)
269 }
270
271 fn visit_array_index(
272 &mut self,
273 array: TaggedItem<'a>,
274 index: u64,
275 ) -> Result<bool, &'static str> {
276 self.0.push(format!("array_index {:?} {}", array, index));
277 Ok(true)
278 }
279
280 fn visit_array_end(&mut self, array: TaggedItem<'a>) -> Result<(), &'static str> {
281 self.0.push(format!("array_end {:?}", array));
282 Ok(())
283 }
284
285 fn visit_dict_begin(
286 &mut self,
287 dict: TaggedItem<'a>,
288 size: Option<u64>,
289 ) -> Result<bool, &'static str> {
290 self.0.push(format!("dict_begin {:?} {:?}", dict, size));
291 Ok(true)
292 }
293
294 fn visit_dict_key(
295 &mut self,
296 dict: TaggedItem<'a>,
297 key: TaggedItem<'a>,
298 is_first: bool,
299 ) -> Result<bool, &'static str> {
300 self.0
301 .push(format!("dict_key {:?} {:?} {}", dict, key, is_first));
302 Ok(true)
303 }
304
305 fn visit_dict_end(&mut self, dict: TaggedItem<'a>) -> Result<(), &'static str> {
306 self.0.push(format!("dict_end {:?}", dict));
307 Ok(())
308 }
309 }
310
311 let mut trace = Vec::new();
312 x.visit(&mut Visitor(trace.as_mut(), false)).unwrap();
313 assert_eq!(
314 trace,
315 vec![
316 "array_begin TaggedItem(Tags(1,2), Array(Some(3))) Some(3)",
317 "array_index TaggedItem(Tags(1,2), Array(Some(3))) 0",
318 "simple TaggedItem(Tags(3,4), Bool(true))",
319 "array_index TaggedItem(Tags(1,2), Array(Some(3))) 1",
320 "dict_begin TaggedItem(Tags(5,6), Dict(Some(2))) Some(2)",
321 "dict_key TaggedItem(Tags(5,6), Dict(Some(2))) TaggedItem(Tags(), Str(k)) true",
322 "simple TaggedItem(Tags(7,8), Pos(5))",
323 "dict_key TaggedItem(Tags(5,6), Dict(Some(2))) TaggedItem(Tags(9,10), Neg(42)) false",
324 "simple TaggedItem(Tags(11,12), Null)",
325 "dict_end TaggedItem(Tags(5,6), Dict(Some(2)))",
326 "array_index TaggedItem(Tags(1,2), Array(Some(3))) 2",
327 "simple TaggedItem(Tags(24), Bytes(82c0f4d82af7))",
328 "array_end TaggedItem(Tags(1,2), Array(Some(3)))",
329 ]
330 );
331
332 trace.clear();
333 assert_eq!(
334 x.visit(&mut Visitor(trace.as_mut(), true)).unwrap_err(),
335 "buh"
336 );
337 assert_eq!(
338 trace,
339 vec![
340 "array_begin TaggedItem(Tags(1,2), Array(Some(3))) Some(3)",
341 "array_index TaggedItem(Tags(1,2), Array(Some(3))) 0",
342 "simple TaggedItem(Tags(3,4), Bool(true))",
343 ]
344 );
345
346 trace.clear();
347 x.index([PathElement::Number(2)])
348 .unwrap()
349 .visit(&mut Visitor(trace.as_mut(), false))
350 .unwrap();
351 assert_eq!(
352 trace,
353 vec![
354 "array_begin TaggedItem(Tags(), Array(Some(2))) Some(2)",
355 "array_index TaggedItem(Tags(), Array(Some(2))) 0",
356 "simple TaggedItem(Tags(0), Bool(false))",
357 "array_index TaggedItem(Tags(), Array(Some(2))) 1",
358 "simple TaggedItem(Tags(42), Undefined)",
359 "array_end TaggedItem(Tags(), Array(Some(2)))",
360 ]
361 );
362
363 assert_eq!(
364 x.index([PathElement::Number(2), PathElement::Number(1)])
365 .unwrap()
366 .kind(),
367 ItemKind::Undefined
368 );
369 }
370}