icydb_core/traits/
validate.rs1use icydb_error::ErrorTree;
2use std::collections::{HashMap, HashSet};
3
4pub trait Validate: ValidateAuto + ValidateCustom {}
9
10impl<T> Validate for T where T: ValidateAuto + ValidateCustom {}
11
12#[derive(Clone, Debug, Default)]
24pub struct ValidateContext;
25
26pub trait ValidateAuto {
37 fn validate_self(&self) -> Result<(), ErrorTree> {
38 Ok(())
39 }
40
41 fn validate_children(&self) -> Result<(), ErrorTree> {
42 Ok(())
43 }
44
45 fn validate_self_with(&self, _ctx: &ValidateContext) -> Result<(), ErrorTree> {
46 self.validate_self()
47 }
48
49 fn validate_children_with(&self, _ctx: &ValidateContext) -> Result<(), ErrorTree> {
50 self.validate_children()
51 }
52}
53
54impl<T: ValidateAuto> ValidateAuto for Box<T> {
55 fn validate_self(&self) -> Result<(), ErrorTree> {
56 (**self).validate_self()
57 }
58
59 fn validate_children(&self) -> Result<(), ErrorTree> {
60 (**self).validate_children()
61 }
62}
63
64impl<T: ValidateAuto> ValidateAuto for Option<T> {
65 fn validate_self(&self) -> Result<(), ErrorTree> {
66 self.as_ref().map_or(Ok(()), ValidateAuto::validate_self)
67 }
68
69 fn validate_children(&self) -> Result<(), ErrorTree> {
70 self.as_ref()
71 .map_or(Ok(()), ValidateAuto::validate_children)
72 }
73}
74
75impl<T: ValidateAuto> ValidateAuto for Vec<T> {
76 fn validate_self(&self) -> Result<(), ErrorTree> {
77 ErrorTree::collect(self.iter().map(ValidateAuto::validate_self))
78 }
79
80 fn validate_children(&self) -> Result<(), ErrorTree> {
81 ErrorTree::collect(self.iter().map(ValidateAuto::validate_children))
82 }
83}
84
85impl<T: ValidateAuto, S> ValidateAuto for HashSet<T, S> {
86 fn validate_self(&self) -> Result<(), ErrorTree> {
87 ErrorTree::collect(self.iter().map(ValidateAuto::validate_self))
88 }
89
90 fn validate_children(&self) -> Result<(), ErrorTree> {
91 ErrorTree::collect(self.iter().map(ValidateAuto::validate_children))
92 }
93}
94
95impl<K: ValidateAuto, V: ValidateAuto, S> ValidateAuto for HashMap<K, V, S> {
96 fn validate_self(&self) -> Result<(), ErrorTree> {
97 ErrorTree::collect(
98 self.iter()
99 .flat_map(|(k, v)| [k.validate_self(), v.validate_self()]),
100 )
101 }
102
103 fn validate_children(&self) -> Result<(), ErrorTree> {
104 ErrorTree::collect(
105 self.iter()
106 .flat_map(|(k, v)| [k.validate_children(), v.validate_children()]),
107 )
108 }
109}
110
111impl_primitive!(ValidateAuto);
112
113pub trait ValidateCustom {
120 fn validate_custom(&self) -> Result<(), ErrorTree> {
121 Ok(())
122 }
123
124 fn validate_custom_with(&self, _ctx: &ValidateContext) -> Result<(), ErrorTree> {
125 self.validate_custom()
126 }
127}
128
129impl<T: ValidateCustom> ValidateCustom for Box<T> {
130 fn validate_custom(&self) -> Result<(), ErrorTree> {
131 (**self).validate_custom()
132 }
133}
134
135impl<T: ValidateCustom> ValidateCustom for Option<T> {
136 fn validate_custom(&self) -> Result<(), ErrorTree> {
137 self.as_ref()
138 .map_or(Ok(()), ValidateCustom::validate_custom)
139 }
140}
141
142impl<T: ValidateCustom> ValidateCustom for Vec<T> {
143 fn validate_custom(&self) -> Result<(), ErrorTree> {
144 ErrorTree::collect(self.iter().map(ValidateCustom::validate_custom))
145 }
146}
147
148impl<T: ValidateCustom, S> ValidateCustom for HashSet<T, S> {
149 fn validate_custom(&self) -> Result<(), ErrorTree> {
150 ErrorTree::collect(self.iter().map(ValidateCustom::validate_custom))
151 }
152}
153
154impl<K: ValidateCustom, V: ValidateCustom, S> ValidateCustom for HashMap<K, V, S> {
155 fn validate_custom(&self) -> Result<(), ErrorTree> {
156 ErrorTree::collect(
157 self.iter()
158 .flat_map(|(k, v)| [k.validate_custom(), v.validate_custom()]),
159 )
160 }
161}
162
163impl_primitive!(ValidateCustom);
164
165#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[derive(Debug, Eq, Hash, PartialEq)]
175 struct Bad;
176
177 impl ValidateAuto for Bad {
178 fn validate_self(&self) -> Result<(), ErrorTree> {
179 Err("bad self".into())
180 }
181 fn validate_children(&self) -> Result<(), ErrorTree> {
182 Err("bad children".into())
183 }
184 }
185
186 impl ValidateCustom for Bad {
187 fn validate_custom(&self) -> Result<(), ErrorTree> {
188 Err("bad custom".into())
189 }
190 }
191
192 #[test]
193 #[allow(clippy::zero_sized_map_values)]
194 fn hashmap_collects_key_and_value_errors() {
195 let mut map = HashMap::new();
196 map.insert(Bad, Bad);
197
198 let result = map.validate_self();
200
201 assert!(result.is_err(), "expected error from validation");
202 let errs = result.unwrap_err();
203
204 let flat = errs.flatten_ref();
206
207 assert!(
209 flat.iter().any(|(_, msg)| msg.contains("bad self")),
210 "missing key/value self errors: {flat:?}",
211 );
212 }
213
214 #[test]
215 #[allow(clippy::zero_sized_map_values)]
216 fn hashmap_collects_custom_errors() {
217 let mut map = HashMap::new();
218 map.insert(Bad, Bad);
219
220 let result = map.validate_custom();
221
222 assert!(result.is_err(), "expected error from custom validation");
223 let errs = result.unwrap_err();
224
225 let flat = errs.flatten_ref();
226 assert!(
227 flat.iter().any(|(_, msg)| msg.contains("bad custom")),
228 "missing key/value custom errors: {flat:?}",
229 );
230 }
231}