icydb_core/traits/
visitable.rs

1use crate::{
2    traits::{Sanitize, Validate},
3    visitor::{PathSegment, Visitor, VisitorMut, perform_visit, perform_visit_mut},
4};
5use std::{
6    collections::{HashMap, HashSet},
7    hash::BuildHasher,
8};
9
10///
11/// Visitable
12///
13
14pub trait Visitable: Sanitize + Validate {
15    fn drive(&self, _: &mut dyn Visitor) {}
16    fn drive_mut(&mut self, _: &mut dyn VisitorMut) {}
17}
18
19impl<T: Visitable> Visitable for Option<T> {
20    fn drive(&self, visitor: &mut dyn Visitor) {
21        if let Some(value) = self {
22            perform_visit(visitor, value, PathSegment::Empty);
23        }
24    }
25
26    fn drive_mut(&mut self, visitor: &mut dyn VisitorMut) {
27        if let Some(value) = self {
28            perform_visit_mut(visitor, value, PathSegment::Empty);
29        }
30    }
31}
32
33impl<T: Visitable> Visitable for Vec<T> {
34    fn drive(&self, visitor: &mut dyn Visitor) {
35        for (i, value) in self.iter().enumerate() {
36            perform_visit(visitor, value, i);
37        }
38    }
39
40    fn drive_mut(&mut self, visitor: &mut dyn VisitorMut) {
41        for (i, value) in self.iter_mut().enumerate() {
42            perform_visit_mut(visitor, value, i);
43        }
44    }
45}
46
47impl<T: Visitable> Visitable for Box<T> {
48    fn drive(&self, visitor: &mut dyn Visitor) {
49        // A Box is just a heap-allocated wrapper.
50        // Delegate directly to the inner value.
51        (**self).drive(visitor);
52    }
53
54    fn drive_mut(&mut self, visitor: &mut dyn VisitorMut) {
55        // Same logic as `drive`, but with mutable access.
56        (**self).drive_mut(visitor);
57    }
58}
59
60impl<T, S> Visitable for HashSet<T, S>
61where
62    T: Visitable + Eq + std::hash::Hash,
63    S: BuildHasher + Default,
64{
65    fn drive(&self, visitor: &mut dyn Visitor) {
66        // Sets don’t have stable ordering or indices.
67        // We still traverse all items, but don’t add a path segment.
68        for item in self {
69            perform_visit(visitor, item, PathSegment::Empty);
70        }
71    }
72
73    fn drive_mut(&mut self, visitor: &mut dyn VisitorMut) {
74        // Drain moves items out so we can mutate them safely
75        // (keys in a set are values, so they can change).
76        let mut new_set = Self::with_hasher(S::default());
77
78        for mut item in self.drain() {
79            perform_visit_mut(visitor, &mut item, PathSegment::Empty);
80            new_set.insert(item);
81        }
82
83        *self = new_set;
84    }
85}
86
87impl<K, V, S> Visitable for HashMap<K, V, S>
88where
89    K: Visitable + Eq + std::hash::Hash,
90    V: Visitable,
91    S: BuildHasher + Default,
92{
93    fn drive(&self, visitor: &mut dyn Visitor) {
94        // Traverse both key and value with explicit path segments
95        for (k, v) in self {
96            perform_visit(visitor, k, "key");
97            perform_visit(visitor, v, "value");
98        }
99    }
100
101    fn drive_mut(&mut self, visitor: &mut dyn VisitorMut) {
102        // Drain ensures we can mutate keys safely without UB.
103        // We rebuild the map after visiting keys + values.
104        let mut new_map = Self::with_hasher(S::default());
105
106        for (mut k, mut v) in self.drain() {
107            perform_visit_mut(visitor, &mut k, "key");
108            perform_visit_mut(visitor, &mut v, "value");
109            new_map.insert(k, v);
110        }
111
112        *self = new_map;
113    }
114}
115
116impl_primitive!(Visitable);