1use crate::visitor::{
7 PathSegment, VisitorContext, VisitorCore, VisitorMutCore, perform_visit, perform_visit_mut,
8};
9
10pub trait Visitable: Normalize + Validate {
23 fn type_identity(&self) -> &'static str {
26 std::any::type_name::<Self>()
27 }
28
29 fn drive(&self, _: &mut dyn VisitorCore) {}
30 fn drive_mut(&mut self, _: &mut dyn VisitorMutCore) {}
31}
32
33impl<T: Visitable> Visitable for Option<T> {
42 fn drive(&self, visitor: &mut dyn VisitorCore) {
43 if let Some(value) = self.as_ref() {
44 perform_visit(visitor, value, PathSegment::Empty);
45 }
46 }
47
48 fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
49 if let Some(value) = self.as_mut() {
50 perform_visit_mut(visitor, value, PathSegment::Empty);
51 }
52 }
53}
54
55impl<T: Visitable> Visitable for Vec<T> {
56 fn drive(&self, visitor: &mut dyn VisitorCore) {
57 for (i, value) in self.iter().enumerate() {
58 perform_visit(visitor, value, i);
59 }
60 }
61
62 fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
63 for (i, value) in self.iter_mut().enumerate() {
64 perform_visit_mut(visitor, value, i);
65 }
66 }
67}
68
69impl<T: Visitable> Visitable for Box<T> {
70 fn type_identity(&self) -> &'static str {
71 (**self).type_identity()
72 }
73
74 fn drive(&self, visitor: &mut dyn VisitorCore) {
75 (**self).drive(visitor);
76 }
77
78 fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
79 (**self).drive_mut(visitor);
80 }
81}
82
83macro_rules! impl_primitive_visitable {
85 ($($ty:ty),* $(,)?) => {
86 $(impl Visitable for $ty {})*
87 };
88}
89
90impl_primitive_visitable!(
91 i8,
92 i16,
93 i32,
94 i64,
95 i128,
96 u8,
97 u16,
98 u32,
99 u64,
100 u128,
101 f32,
102 f64,
103 bool,
104 String,
105 crate::schema::Account,
106 crate::schema::Blob,
107 crate::schema::Date,
108 crate::schema::Decimal,
109 crate::schema::Duration,
110 crate::schema::Float32,
111 crate::schema::Float64,
112 crate::schema::IntBig,
113 crate::schema::NatBig,
114 crate::schema::Principal,
115 crate::schema::Subaccount,
116 crate::schema::Timestamp,
117 crate::schema::U256,
118 crate::schema::Ulid,
119 crate::schema::Unit,
120);
121
122pub trait Normalize: NormalizeAuto + NormalizeCustom {}
130
131impl<T> Normalize for T where T: NormalizeAuto + NormalizeCustom {}
132
133pub trait NormalizeAuto {
145 fn normalize_self(&mut self, _ctx: &mut dyn VisitorContext) {}
146}
147
148impl<T: NormalizeAuto> NormalizeAuto for Option<T> {}
149
150impl<T: NormalizeAuto> NormalizeAuto for Vec<T> {}
151
152impl<T: NormalizeAuto + ?Sized> NormalizeAuto for Box<T> {
153 fn normalize_self(&mut self, ctx: &mut dyn VisitorContext) {
154 (**self).normalize_self(ctx);
155 }
156}
157
158impl_primitive!(NormalizeAuto);
159
160pub trait NormalizeCustom {
168 fn normalize_custom(&mut self, _ctx: &mut dyn VisitorContext) {}
169}
170
171impl<T: NormalizeCustom> NormalizeCustom for Option<T> {}
172
173impl<T: NormalizeCustom> NormalizeCustom for Vec<T> {}
174
175impl<T: NormalizeCustom + ?Sized> NormalizeCustom for Box<T> {
176 fn normalize_custom(&mut self, ctx: &mut dyn VisitorContext) {
177 (**self).normalize_custom(ctx);
178 }
179}
180
181impl_primitive!(NormalizeCustom);
182
183pub trait Validate: ValidateAuto + ValidateCustom {}
191
192impl<T> Validate for T where T: ValidateAuto + ValidateCustom {}
193
194pub trait ValidateAuto {
206 fn validate_self(&self, _ctx: &mut dyn VisitorContext) {}
207}
208
209impl<T: ValidateAuto> ValidateAuto for Option<T> {}
210
211impl<T: ValidateAuto> ValidateAuto for Vec<T> {}
212
213impl<T: ValidateAuto + ?Sized> ValidateAuto for Box<T> {
214 fn validate_self(&self, ctx: &mut dyn VisitorContext) {
215 (**self).validate_self(ctx);
216 }
217}
218
219impl_primitive!(ValidateAuto);
220
221pub trait ValidateCustom {
229 fn validate_custom(&self, _ctx: &mut dyn VisitorContext) {}
230}
231
232impl<T: ValidateCustom> ValidateCustom for Option<T> {}
233
234impl<T: ValidateCustom> ValidateCustom for Vec<T> {}
235
236impl<T: ValidateCustom + ?Sized> ValidateCustom for Box<T> {
237 fn validate_custom(&self, ctx: &mut dyn VisitorContext) {
238 (**self).validate_custom(ctx);
239 }
240}
241
242impl_primitive!(ValidateCustom);
243
244pub trait Normalizer<T> {
246 fn normalize(&self, value: &mut T) -> Result<(), String>;
247
248 fn normalize_with_context(
249 &self,
250 value: &mut T,
251 ctx: &mut dyn VisitorContext,
252 ) -> Result<(), String> {
253 let _ = ctx;
254
255 self.normalize(value)
256 }
257}
258
259pub trait Validator<T: ?Sized> {
261 fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::{
268 normalize::normalize,
269 validate::validate,
270 visitor::{ApplicationOperation, CallbackKind, Issue, VisitorError},
271 };
272 use std::{
273 cell::{Cell, RefCell},
274 rc::Rc,
275 };
276
277 const AUTO_NORMALIZE_ISSUE: &str = "automatic normalize";
278 const CUSTOM_NORMALIZE_ISSUE: &str = "custom normalize";
279 const AUTO_VALIDATE_ISSUE: &str = "automatic validate";
280 const CUSTOM_VALIDATE_ISSUE: &str = "custom validate";
281
282 #[derive(Default)]
283 struct HookProbe {
284 auto_normalize: u32,
285 custom_normalize: u32,
286 auto_validate: Cell<u32>,
287 custom_validate: Cell<u32>,
288 }
289
290 struct OrderedLeaf {
291 events: Rc<RefCell<Vec<&'static str>>>,
292 }
293
294 impl Visitable for OrderedLeaf {}
295
296 impl NormalizeAuto for OrderedLeaf {
297 fn normalize_self(&mut self, _ctx: &mut dyn VisitorContext) {
298 self.events.borrow_mut().push("leaf normalize auto");
299 }
300 }
301
302 impl NormalizeCustom for OrderedLeaf {
303 fn normalize_custom(&mut self, _ctx: &mut dyn VisitorContext) {
304 self.events.borrow_mut().push("leaf normalize custom");
305 }
306 }
307
308 impl ValidateAuto for OrderedLeaf {
309 fn validate_self(&self, _ctx: &mut dyn VisitorContext) {
310 self.events.borrow_mut().push("leaf validate auto");
311 }
312 }
313
314 impl ValidateCustom for OrderedLeaf {
315 fn validate_custom(&self, _ctx: &mut dyn VisitorContext) {
316 self.events.borrow_mut().push("leaf validate custom");
317 }
318 }
319
320 struct OrderedParent {
321 events: Rc<RefCell<Vec<&'static str>>>,
322 child: OrderedLeaf,
323 }
324
325 impl Visitable for OrderedParent {
326 fn drive(&self, visitor: &mut dyn VisitorCore) {
327 perform_visit(visitor, &self.child, "child");
328 }
329
330 fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
331 perform_visit_mut(visitor, &mut self.child, "child");
332 }
333 }
334
335 impl NormalizeAuto for OrderedParent {
336 fn normalize_self(&mut self, _ctx: &mut dyn VisitorContext) {
337 self.events.borrow_mut().push("parent normalize auto");
338 }
339 }
340
341 impl NormalizeCustom for OrderedParent {
342 fn normalize_custom(&mut self, _ctx: &mut dyn VisitorContext) {
343 self.events.borrow_mut().push("parent normalize custom");
344 }
345 }
346
347 impl ValidateAuto for OrderedParent {
348 fn validate_self(&self, _ctx: &mut dyn VisitorContext) {
349 self.events.borrow_mut().push("parent validate auto");
350 }
351 }
352
353 impl ValidateCustom for OrderedParent {
354 fn validate_custom(&self, _ctx: &mut dyn VisitorContext) {
355 self.events.borrow_mut().push("parent validate custom");
356 }
357 }
358
359 fn ordered_parent() -> (OrderedParent, Rc<RefCell<Vec<&'static str>>>) {
360 let events = Rc::new(RefCell::new(Vec::new()));
361 (
362 OrderedParent {
363 events: Rc::clone(&events),
364 child: OrderedLeaf {
365 events: Rc::clone(&events),
366 },
367 },
368 events,
369 )
370 }
371
372 impl Visitable for HookProbe {}
373
374 impl NormalizeAuto for HookProbe {
375 fn normalize_self(&mut self, ctx: &mut dyn VisitorContext) {
376 self.auto_normalize += 1;
377 ctx.issue(AUTO_NORMALIZE_ISSUE);
378 }
379 }
380
381 impl NormalizeCustom for HookProbe {
382 fn normalize_custom(&mut self, ctx: &mut dyn VisitorContext) {
383 self.custom_normalize += 1;
384 ctx.issue(CUSTOM_NORMALIZE_ISSUE);
385 }
386 }
387
388 impl ValidateAuto for HookProbe {
389 fn validate_self(&self, ctx: &mut dyn VisitorContext) {
390 self.auto_validate.set(self.auto_validate.get() + 1);
391 ctx.issue(AUTO_VALIDATE_ISSUE);
392 }
393 }
394
395 impl ValidateCustom for HookProbe {
396 fn validate_custom(&self, ctx: &mut dyn VisitorContext) {
397 self.custom_validate.set(self.custom_validate.get() + 1);
398 ctx.issue(CUSTOM_VALIDATE_ISSUE);
399 }
400 }
401
402 fn assert_issues(error: &VisitorError, path: &str, expected: [&str; 2]) {
403 let issues = error
404 .issues()
405 .get(path)
406 .unwrap_or_else(|| panic!("expected visitor issues at {path}"));
407 let messages = issues.iter().map(Issue::message).collect::<Vec<_>>();
408 assert_eq!(messages, expected);
409 }
410
411 fn assert_callbacks(error: &VisitorError, path: &str, expected: [CallbackKind; 2]) {
412 let issues = error
413 .issues()
414 .get(path)
415 .unwrap_or_else(|| panic!("expected visitor issues at {path}"));
416 let callbacks = issues
417 .iter()
418 .map(|issue| {
419 issue
420 .callback()
421 .expect("top-level application traversal must type every callback")
422 })
423 .collect::<Vec<_>>();
424 assert_eq!(
425 callbacks
426 .iter()
427 .map(|callback| callback.kind())
428 .collect::<Vec<_>>(),
429 expected
430 );
431 assert!(
432 callbacks
433 .iter()
434 .all(|callback| callback.type_path() == std::any::type_name::<HookProbe>())
435 );
436 }
437
438 #[test]
439 fn option_vec_normalize_hooks_run_once_at_each_indexed_path() {
440 let mut value = Some(vec![HookProbe::default(), HookProbe::default()]);
441
442 let error = normalize(&mut value).expect_err("probe normalizers should report issues");
443 assert_eq!(error.operation(), ApplicationOperation::Normalize);
444
445 let Some(probes) = value.as_ref() else {
446 panic!("normalize should preserve the populated option");
447 };
448 for probe in probes {
449 assert_eq!(probe.auto_normalize, 1);
450 assert_eq!(probe.custom_normalize, 1);
451 }
452 assert!(error.issues().get("").is_none());
453 assert_issues(
454 &error,
455 "[0]",
456 [AUTO_NORMALIZE_ISSUE, CUSTOM_NORMALIZE_ISSUE],
457 );
458 assert_callbacks(
459 &error,
460 "[0]",
461 [CallbackKind::NormalizeAuto, CallbackKind::NormalizeCustom],
462 );
463 assert_issues(
464 &error,
465 "[1]",
466 [AUTO_NORMALIZE_ISSUE, CUSTOM_NORMALIZE_ISSUE],
467 );
468 }
469
470 #[test]
471 fn option_vec_validate_hooks_run_once_at_each_indexed_path() {
472 let value = Some(vec![HookProbe::default(), HookProbe::default()]);
473
474 let error = validate(&value).expect_err("probe validators should report issues");
475 assert_eq!(error.operation(), ApplicationOperation::Validate);
476
477 let Some(probes) = value.as_ref() else {
478 panic!("validate should preserve the populated option");
479 };
480 for probe in probes {
481 assert_eq!(probe.auto_validate.get(), 1);
482 assert_eq!(probe.custom_validate.get(), 1);
483 }
484 assert!(error.issues().get("").is_none());
485 assert_issues(&error, "[0]", [AUTO_VALIDATE_ISSUE, CUSTOM_VALIDATE_ISSUE]);
486 assert_callbacks(
487 &error,
488 "[0]",
489 [CallbackKind::ValidateAuto, CallbackKind::ValidateCustom],
490 );
491 assert_issues(&error, "[1]", [AUTO_VALIDATE_ISSUE, CUSTOM_VALIDATE_ISSUE]);
492 }
493
494 #[test]
495 fn box_transparency_keeps_one_forwarded_hook_call() {
496 let mut normalized = Box::new(HookProbe::default());
497 let normalize_error =
498 normalize(&mut normalized).expect_err("probe normalizers should report issues");
499 assert_eq!(normalized.auto_normalize, 1);
500 assert_eq!(normalized.custom_normalize, 1);
501 assert_callbacks(
502 &normalize_error,
503 "",
504 [CallbackKind::NormalizeAuto, CallbackKind::NormalizeCustom],
505 );
506
507 let validated = Box::new(HookProbe::default());
508 let validate_error =
509 validate(&validated).expect_err("probe validators should report issues");
510 assert_eq!(validated.auto_validate.get(), 1);
511 assert_eq!(validated.custom_validate.get(), 1);
512 assert_callbacks(
513 &validate_error,
514 "",
515 [CallbackKind::ValidateAuto, CallbackKind::ValidateCustom],
516 );
517 }
518
519 #[test]
520 fn normalize_and_validate_traversals_are_preorder_and_declaration_ordered() {
521 let (mut normalized, normalize_events) = ordered_parent();
522 normalize(&mut normalized).expect("ordered normalizers should succeed");
523 assert_eq!(
524 normalize_events.borrow().as_slice(),
525 [
526 "parent normalize auto",
527 "parent normalize custom",
528 "leaf normalize auto",
529 "leaf normalize custom",
530 ]
531 );
532
533 let (validated, validate_events) = ordered_parent();
534 validate(&validated).expect("ordered validators should succeed");
535 assert_eq!(
536 validate_events.borrow().as_slice(),
537 [
538 "parent validate auto",
539 "parent validate custom",
540 "leaf validate auto",
541 "leaf validate custom",
542 ]
543 );
544 }
545}