1 | //! Value inspection. |
2 | //! |
3 | //! The [`Visit`] trait provides a simple visitor API that can be used to inspect |
4 | //! the structure of primitives stored in a [`ValueBag`](../struct.ValueBag.html). |
5 | //! More complex datatypes can then be handled using `std::fmt`, `sval`, or `serde`. |
6 | //! |
7 | //! ``` |
8 | //! # #[cfg (not(feature = "std" ))] fn main() {} |
9 | //! # #[cfg (feature = "std" )] |
10 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
11 | //! # fn escape(buf: &[u8]) -> &[u8] { buf } |
12 | //! # fn itoa_fmt<T>(num: T) -> Vec<u8> { vec![] } |
13 | //! # fn ryu_fmt<T>(num: T) -> Vec<u8> { vec![] } |
14 | //! # use std::io::Write; |
15 | //! use value_bag::{ValueBag, Error, visit::Visit}; |
16 | //! |
17 | //! // Implement some simple custom serialization |
18 | //! struct MyVisit(Vec<u8>); |
19 | //! impl<'v> Visit<'v> for MyVisit { |
20 | //! fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { |
21 | //! // Fallback to `Debug` if we didn't visit the value specially |
22 | //! write!(&mut self.0, "{:?}" , v).map_err(|_| Error::msg("failed to write value" )) |
23 | //! } |
24 | //! |
25 | //! fn visit_u64(&mut self, v: u64) -> Result<(), Error> { |
26 | //! self.0.extend_from_slice(itoa_fmt(v).as_slice()); |
27 | //! Ok(()) |
28 | //! } |
29 | //! |
30 | //! fn visit_i64(&mut self, v: i64) -> Result<(), Error> { |
31 | //! self.0.extend_from_slice(itoa_fmt(v).as_slice()); |
32 | //! Ok(()) |
33 | //! } |
34 | //! |
35 | //! fn visit_f64(&mut self, v: f64) -> Result<(), Error> { |
36 | //! self.0.extend_from_slice(ryu_fmt(v).as_slice()); |
37 | //! Ok(()) |
38 | //! } |
39 | //! |
40 | //! fn visit_str(&mut self, v: &str) -> Result<(), Error> { |
41 | //! self.0.push(b' \"' ); |
42 | //! self.0.extend_from_slice(escape(v.as_bytes())); |
43 | //! self.0.push(b' \"' ); |
44 | //! Ok(()) |
45 | //! } |
46 | //! |
47 | //! fn visit_bool(&mut self, v: bool) -> Result<(), Error> { |
48 | //! self.0.extend_from_slice(if v { b"true" } else { b"false" }); |
49 | //! Ok(()) |
50 | //! } |
51 | //! } |
52 | //! |
53 | //! let value = ValueBag::from(42i64); |
54 | //! |
55 | //! let mut visitor = MyVisit(vec![]); |
56 | //! value.visit(&mut visitor)?; |
57 | //! # Ok(()) |
58 | //! # } |
59 | //! ``` |
60 | |
61 | use crate::{ |
62 | internal::{self, InternalVisitor}, |
63 | Error, ValueBag, |
64 | }; |
65 | |
66 | /// A visitor for a `ValueBag`. |
67 | pub trait Visit<'v> { |
68 | /// Visit a `ValueBag`. |
69 | /// |
70 | /// This is the only required method on `Visit` and acts as a fallback for any |
71 | /// more specific methods that aren't overridden. |
72 | /// The `ValueBag` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, |
73 | /// or serialized using its `sval::Value` or `serde::Serialize` implementation. |
74 | fn visit_any(&mut self, value: ValueBag) -> Result<(), Error>; |
75 | |
76 | /// Visit an empty value. |
77 | #[inline ] |
78 | fn visit_empty(&mut self) -> Result<(), Error> { |
79 | self.visit_any(ValueBag::empty()) |
80 | } |
81 | |
82 | /// Visit an unsigned integer. |
83 | #[inline ] |
84 | fn visit_u64(&mut self, value: u64) -> Result<(), Error> { |
85 | self.visit_any(value.into()) |
86 | } |
87 | |
88 | /// Visit a signed integer. |
89 | #[inline ] |
90 | fn visit_i64(&mut self, value: i64) -> Result<(), Error> { |
91 | self.visit_any(value.into()) |
92 | } |
93 | |
94 | /// Visit a big unsigned integer. |
95 | #[inline ] |
96 | fn visit_u128(&mut self, value: u128) -> Result<(), Error> { |
97 | self.visit_any((&value).into()) |
98 | } |
99 | |
100 | /// Visit a big signed integer. |
101 | #[inline ] |
102 | fn visit_i128(&mut self, value: i128) -> Result<(), Error> { |
103 | self.visit_any((&value).into()) |
104 | } |
105 | |
106 | /// Visit a floating point. |
107 | #[inline ] |
108 | fn visit_f64(&mut self, value: f64) -> Result<(), Error> { |
109 | self.visit_any(value.into()) |
110 | } |
111 | |
112 | /// Visit a boolean. |
113 | #[inline ] |
114 | fn visit_bool(&mut self, value: bool) -> Result<(), Error> { |
115 | self.visit_any(value.into()) |
116 | } |
117 | |
118 | /// Visit a string. |
119 | #[inline ] |
120 | fn visit_str(&mut self, value: &str) -> Result<(), Error> { |
121 | self.visit_any(value.into()) |
122 | } |
123 | |
124 | /// Visit a string. |
125 | #[inline ] |
126 | fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { |
127 | self.visit_str(value) |
128 | } |
129 | |
130 | /// Visit a Unicode character. |
131 | #[inline ] |
132 | fn visit_char(&mut self, value: char) -> Result<(), Error> { |
133 | let mut b = [0; 4]; |
134 | self.visit_str(&*value.encode_utf8(&mut b)) |
135 | } |
136 | |
137 | /// Visit an error. |
138 | #[inline ] |
139 | #[cfg (feature = "error" )] |
140 | fn visit_error(&mut self, err: &(dyn crate::std::error::Error + 'static)) -> Result<(), Error> { |
141 | self.visit_any(ValueBag::from_dyn_error(err)) |
142 | } |
143 | |
144 | /// Visit an error. |
145 | #[inline ] |
146 | #[cfg (feature = "error" )] |
147 | fn visit_borrowed_error( |
148 | &mut self, |
149 | err: &'v (dyn crate::std::error::Error + 'static), |
150 | ) -> Result<(), Error> { |
151 | self.visit_any(ValueBag::from_dyn_error(err)) |
152 | } |
153 | } |
154 | |
155 | impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T |
156 | where |
157 | T: Visit<'v>, |
158 | { |
159 | #[inline ] |
160 | fn visit_any(&mut self, value: ValueBag) -> Result<(), Error> { |
161 | (**self).visit_any(value) |
162 | } |
163 | |
164 | #[inline ] |
165 | fn visit_empty(&mut self) -> Result<(), Error> { |
166 | (**self).visit_empty() |
167 | } |
168 | |
169 | #[inline ] |
170 | fn visit_u64(&mut self, value: u64) -> Result<(), Error> { |
171 | (**self).visit_u64(value) |
172 | } |
173 | |
174 | #[inline ] |
175 | fn visit_i64(&mut self, value: i64) -> Result<(), Error> { |
176 | (**self).visit_i64(value) |
177 | } |
178 | |
179 | #[inline ] |
180 | fn visit_u128(&mut self, value: u128) -> Result<(), Error> { |
181 | (**self).visit_u128(value) |
182 | } |
183 | |
184 | #[inline ] |
185 | fn visit_i128(&mut self, value: i128) -> Result<(), Error> { |
186 | (**self).visit_i128(value) |
187 | } |
188 | |
189 | #[inline ] |
190 | fn visit_f64(&mut self, value: f64) -> Result<(), Error> { |
191 | (**self).visit_f64(value) |
192 | } |
193 | |
194 | #[inline ] |
195 | fn visit_bool(&mut self, value: bool) -> Result<(), Error> { |
196 | (**self).visit_bool(value) |
197 | } |
198 | |
199 | #[inline ] |
200 | fn visit_str(&mut self, value: &str) -> Result<(), Error> { |
201 | (**self).visit_str(value) |
202 | } |
203 | |
204 | #[inline ] |
205 | fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { |
206 | (**self).visit_borrowed_str(value) |
207 | } |
208 | |
209 | #[inline ] |
210 | fn visit_char(&mut self, value: char) -> Result<(), Error> { |
211 | (**self).visit_char(value) |
212 | } |
213 | |
214 | #[inline ] |
215 | #[cfg (feature = "error" )] |
216 | fn visit_error(&mut self, err: &(dyn crate::std::error::Error + 'static)) -> Result<(), Error> { |
217 | (**self).visit_error(err) |
218 | } |
219 | |
220 | #[inline ] |
221 | #[cfg (feature = "error" )] |
222 | fn visit_borrowed_error( |
223 | &mut self, |
224 | err: &'v (dyn crate::std::error::Error + 'static), |
225 | ) -> Result<(), Error> { |
226 | (**self).visit_borrowed_error(err) |
227 | } |
228 | } |
229 | |
230 | impl<'v> ValueBag<'v> { |
231 | /// Visit this value using a simple visitor. |
232 | /// |
233 | /// The visitor isn't strictly required to inspect the contents of a value bag. |
234 | /// It's useful for simple cases where a full framework like `serde` or `sval` |
235 | /// isn't necessary. |
236 | pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { |
237 | struct Visitor<V>(V); |
238 | |
239 | impl<'v, V> InternalVisitor<'v> for Visitor<V> |
240 | where |
241 | V: Visit<'v>, |
242 | { |
243 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { |
244 | v.fill(crate::fill::Slot::new(self)) |
245 | } |
246 | |
247 | fn debug(&mut self, v: &dyn internal::fmt::Debug) -> Result<(), Error> { |
248 | self.0.visit_any(ValueBag::from_dyn_debug(v)) |
249 | } |
250 | |
251 | fn display(&mut self, v: &dyn internal::fmt::Display) -> Result<(), Error> { |
252 | self.0.visit_any(ValueBag::from_dyn_display(v)) |
253 | } |
254 | |
255 | fn u64(&mut self, v: u64) -> Result<(), Error> { |
256 | self.0.visit_u64(v) |
257 | } |
258 | |
259 | fn i64(&mut self, v: i64) -> Result<(), Error> { |
260 | self.0.visit_i64(v) |
261 | } |
262 | |
263 | fn u128(&mut self, v: &u128) -> Result<(), Error> { |
264 | self.0.visit_u128(*v) |
265 | } |
266 | |
267 | fn i128(&mut self, v: &i128) -> Result<(), Error> { |
268 | self.0.visit_i128(*v) |
269 | } |
270 | |
271 | fn f64(&mut self, v: f64) -> Result<(), Error> { |
272 | self.0.visit_f64(v) |
273 | } |
274 | |
275 | fn bool(&mut self, v: bool) -> Result<(), Error> { |
276 | self.0.visit_bool(v) |
277 | } |
278 | |
279 | fn char(&mut self, v: char) -> Result<(), Error> { |
280 | self.0.visit_char(v) |
281 | } |
282 | |
283 | fn str(&mut self, v: &str) -> Result<(), Error> { |
284 | self.0.visit_str(v) |
285 | } |
286 | |
287 | fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { |
288 | self.0.visit_borrowed_str(v) |
289 | } |
290 | |
291 | fn none(&mut self) -> Result<(), Error> { |
292 | self.0.visit_empty() |
293 | } |
294 | |
295 | #[cfg (feature = "error" )] |
296 | fn error(&mut self, v: &(dyn internal::error::Error + 'static)) -> Result<(), Error> { |
297 | self.0.visit_error(v) |
298 | } |
299 | |
300 | #[cfg (feature = "error" )] |
301 | fn borrowed_error( |
302 | &mut self, |
303 | v: &'v (dyn internal::error::Error + 'static), |
304 | ) -> Result<(), Error> { |
305 | self.0.visit_borrowed_error(v) |
306 | } |
307 | |
308 | #[cfg (feature = "sval2" )] |
309 | fn sval2(&mut self, v: &dyn internal::sval::v2::Value) -> Result<(), Error> { |
310 | if internal::sval::v2::internal_visit(v, self) { |
311 | Ok(()) |
312 | } else { |
313 | self.0.visit_any(ValueBag::from_dyn_sval2(v)) |
314 | } |
315 | } |
316 | |
317 | #[cfg (feature = "sval2" )] |
318 | fn borrowed_sval2( |
319 | &mut self, |
320 | v: &'v dyn internal::sval::v2::Value, |
321 | ) -> Result<(), Error> { |
322 | if internal::sval::v2::borrowed_internal_visit(v, self) { |
323 | Ok(()) |
324 | } else { |
325 | self.0.visit_any(ValueBag::from_dyn_sval2(v)) |
326 | } |
327 | } |
328 | |
329 | #[cfg (feature = "serde1" )] |
330 | fn serde1(&mut self, v: &dyn internal::serde::v1::Serialize) -> Result<(), Error> { |
331 | if internal::serde::v1::internal_visit(v, self) { |
332 | Ok(()) |
333 | } else { |
334 | self.0.visit_any(ValueBag::from_dyn_serde1(v)) |
335 | } |
336 | } |
337 | |
338 | #[cfg (feature = "seq" )] |
339 | fn seq(&mut self, v: &dyn internal::seq::Seq) -> Result<(), Error> { |
340 | self.0.visit_any(ValueBag::from_dyn_seq(v)) |
341 | } |
342 | |
343 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { |
344 | Err(Error::msg(msg)) |
345 | } |
346 | } |
347 | |
348 | self.internal_visit(&mut Visitor(visitor)) |
349 | } |
350 | } |
351 | |
352 | #[cfg (test)] |
353 | mod tests { |
354 | use super::*; |
355 | use crate::test::*; |
356 | |
357 | #[cfg (target_arch = "wasm32" )] |
358 | use wasm_bindgen_test::*; |
359 | |
360 | #[test ] |
361 | #[cfg_attr (target_arch = "wasm32" , wasm_bindgen_test)] |
362 | fn visit_structured() { |
363 | ValueBag::from(42u64) |
364 | .visit(TestVisit::default()) |
365 | .expect("failed to visit value" ); |
366 | ValueBag::from(-42i64) |
367 | .visit(TestVisit::default()) |
368 | .expect("failed to visit value" ); |
369 | ValueBag::from(&42u128) |
370 | .visit(TestVisit::default()) |
371 | .expect("failed to visit value" ); |
372 | ValueBag::from(&-42i128) |
373 | .visit(TestVisit::default()) |
374 | .expect("failed to visit value" ); |
375 | ValueBag::from(11f64) |
376 | .visit(TestVisit::default()) |
377 | .expect("failed to visit value" ); |
378 | ValueBag::from(true) |
379 | .visit(TestVisit::default()) |
380 | .expect("failed to visit value" ); |
381 | ValueBag::from("some borrowed string" ) |
382 | .visit(TestVisit::default()) |
383 | .expect("failed to visit value" ); |
384 | ValueBag::from('n' ) |
385 | .visit(TestVisit::default()) |
386 | .expect("failed to visit value" ); |
387 | } |
388 | |
389 | #[test ] |
390 | #[cfg_attr (target_arch = "wasm32" , wasm_bindgen_test)] |
391 | fn visit_empty() { |
392 | struct Visitor(bool); |
393 | |
394 | impl<'v> Visit<'v> for Visitor { |
395 | fn visit_any(&mut self, _: ValueBag) -> Result<(), Error> { |
396 | Ok(()) |
397 | } |
398 | |
399 | fn visit_empty(&mut self) -> Result<(), Error> { |
400 | self.0 = true; |
401 | Ok(()) |
402 | } |
403 | } |
404 | |
405 | let mut visitor = Visitor(false); |
406 | ValueBag::empty().visit(&mut visitor).unwrap(); |
407 | |
408 | assert!(visitor.0); |
409 | } |
410 | |
411 | #[test ] |
412 | #[cfg (feature = "serde1" )] |
413 | #[cfg_attr (target_arch = "wasm32" , wasm_bindgen_test)] |
414 | fn visit_serde1() { |
415 | use crate::std::string::{String, ToString}; |
416 | |
417 | struct Data; |
418 | |
419 | impl value_bag_serde1::lib::Serialize for Data { |
420 | fn serialize<S: value_bag_serde1::lib::Serializer>( |
421 | &self, |
422 | serializer: S, |
423 | ) -> Result<S::Ok, S::Error> { |
424 | use value_bag_serde1::lib::ser::SerializeStruct; |
425 | |
426 | let mut s = serializer.serialize_struct("Data" , 3)?; |
427 | s.serialize_field("a" , &1)?; |
428 | s.serialize_field("b" , &2)?; |
429 | s.serialize_field("c" , &3)?; |
430 | s.end() |
431 | } |
432 | } |
433 | |
434 | struct Visitor(String); |
435 | |
436 | impl<'v> Visit<'v> for Visitor { |
437 | fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { |
438 | self.0 = v.to_string(); |
439 | |
440 | Ok(()) |
441 | } |
442 | } |
443 | |
444 | let mut visitor = Visitor("" .into()); |
445 | ValueBag::from_serde1(&Data).visit(&mut visitor).unwrap(); |
446 | |
447 | assert_eq!("Data { a: 1, b: 2, c: 3 }" , visitor.0); |
448 | } |
449 | |
450 | #[test ] |
451 | #[cfg (feature = "sval2" )] |
452 | #[cfg_attr (target_arch = "wasm32" , wasm_bindgen_test)] |
453 | fn visit_sval2() { |
454 | use crate::std::string::{String, ToString}; |
455 | |
456 | struct Data; |
457 | |
458 | impl value_bag_sval2::lib::Value for Data { |
459 | fn stream<'sval, S: value_bag_sval2::lib::Stream<'sval> + ?Sized>( |
460 | &'sval self, |
461 | stream: &mut S, |
462 | ) -> value_bag_sval2::lib::Result { |
463 | stream.map_begin(Some(3))?; |
464 | |
465 | stream.map_key_begin()?; |
466 | stream.value("a" )?; |
467 | stream.map_key_end()?; |
468 | |
469 | stream.map_value_begin()?; |
470 | stream.value(&1)?; |
471 | stream.map_value_end()?; |
472 | |
473 | stream.map_key_begin()?; |
474 | stream.value("b" )?; |
475 | stream.map_key_end()?; |
476 | |
477 | stream.map_value_begin()?; |
478 | stream.value(&2)?; |
479 | stream.map_value_end()?; |
480 | |
481 | stream.map_key_begin()?; |
482 | stream.value("c" )?; |
483 | stream.map_key_end()?; |
484 | |
485 | stream.map_value_begin()?; |
486 | stream.value(&3)?; |
487 | stream.map_value_end()?; |
488 | |
489 | stream.map_end() |
490 | } |
491 | } |
492 | |
493 | struct Visitor(String); |
494 | |
495 | impl<'v> Visit<'v> for Visitor { |
496 | fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { |
497 | self.0 = v.to_string(); |
498 | |
499 | Ok(()) |
500 | } |
501 | } |
502 | |
503 | let mut visitor = Visitor("" .into()); |
504 | ValueBag::from_sval2(&Data).visit(&mut visitor).unwrap(); |
505 | |
506 | assert_eq!("{ \"a \": 1, \"b \": 2, \"c \": 3 }" , visitor.0); |
507 | } |
508 | } |
509 | |