1use std::fmt::Debug;
4
5use crate::error::{Path, PathComponentKind};
6use crate::Report;
7
8pub trait Validate {
13 type Context;
17
18 fn validate(&self) -> Result<(), Report>
24 where
25 Self::Context: Default,
26 {
27 let ctx = Self::Context::default();
28 self.validate_with(&ctx)
29 }
30
31 fn validate_with(&self, ctx: &Self::Context) -> Result<(), Report> {
37 let mut report = Report::new();
38 self.validate_into(ctx, &mut Path::empty, &mut report);
39 match report.is_empty() {
40 true => Ok(()),
41 false => Err(report),
42 }
43 }
44
45 fn validate_into(
47 &self,
48 ctx: &Self::Context,
49 parent: &mut dyn FnMut() -> Path,
50 report: &mut Report,
51 );
52}
53
54#[derive(Debug, Clone, Copy)]
61pub struct Valid<T>(T);
62
63impl<T: Validate> Valid<T> {
64 pub fn into_inner(self) -> T {
66 self.0
67 }
68}
69
70impl<T> std::ops::Deref for Valid<T> {
71 type Target = T;
72
73 fn deref(&self) -> &Self::Target {
74 &self.0
75 }
76}
77
78#[derive(Clone, Copy, Default)]
82#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
83#[cfg_attr(feature = "serde", serde(transparent))]
84#[repr(transparent)]
85pub struct Unvalidated<T>(T);
86
87impl<T: Validate> Unvalidated<T> {
88 pub fn new(v: T) -> Self {
90 Self(v)
91 }
92
93 pub fn validate(self) -> Result<Valid<T>, Report>
96 where
97 <T as Validate>::Context: Default,
98 {
99 self.0.validate()?;
100 Ok(Valid(self.0))
101 }
102
103 pub fn validate_with(self, ctx: &<T as Validate>::Context) -> Result<Valid<T>, Report> {
106 self.0.validate_with(ctx)?;
107 Ok(Valid(self.0))
108 }
109}
110
111impl<T: Validate> From<T> for Unvalidated<T> {
112 fn from(value: T) -> Self {
113 Self(value)
114 }
115}
116
117impl<T: Debug> Debug for Unvalidated<T> {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 Debug::fmt(&self.0, f)
120 }
121}
122
123impl<T: ?Sized + Validate> Validate for &T {
124 type Context = T::Context;
125
126 fn validate_into(
127 &self,
128 ctx: &Self::Context,
129 parent: &mut dyn FnMut() -> Path,
130 report: &mut Report,
131 ) {
132 <T as Validate>::validate_into(self, ctx, parent, report)
133 }
134}
135
136impl<T: ?Sized + Validate> Validate for &mut T {
137 type Context = T::Context;
138
139 fn validate_into(
140 &self,
141 ctx: &Self::Context,
142 parent: &mut dyn FnMut() -> Path,
143 report: &mut Report,
144 ) {
145 <T as Validate>::validate_into(self, ctx, parent, report)
146 }
147}
148
149impl<T: Validate> Validate for std::boxed::Box<T> {
150 type Context = T::Context;
151
152 fn validate_into(
153 &self,
154 ctx: &Self::Context,
155 parent: &mut dyn FnMut() -> Path,
156 report: &mut Report,
157 ) {
158 <T as Validate>::validate_into(self, ctx, parent, report)
159 }
160}
161
162impl<T: Validate> Validate for std::rc::Rc<T> {
163 type Context = T::Context;
164
165 fn validate_into(
166 &self,
167 ctx: &Self::Context,
168 parent: &mut dyn FnMut() -> Path,
169 report: &mut Report,
170 ) {
171 <T as Validate>::validate_into(self, ctx, parent, report)
172 }
173}
174
175impl<T: Validate> Validate for std::sync::Arc<T> {
176 type Context = T::Context;
177
178 fn validate_into(
179 &self,
180 ctx: &Self::Context,
181 parent: &mut dyn FnMut() -> Path,
182 report: &mut Report,
183 ) {
184 <T as Validate>::validate_into(self, ctx, parent, report)
185 }
186}
187
188macro_rules! impl_validate_list {
189 (<$T:ident $(, $Other:ident)*> $Container:ty) => {
190 impl<$T, $($Other),*> Validate for $Container
191 where
192 $T: Validate
193 {
194 type Context = T::Context;
195
196 fn validate_into(&self, ctx: &Self::Context, mut parent: &mut dyn FnMut() -> Path, report: &mut Report) {
197 for (index, item) in self.iter().enumerate() {
198 let mut path = $crate::util::nested_path!(parent, index);
199 <T as Validate>::validate_into(item, ctx, &mut path, report);
200 }
201 }
202 }
203 };
204}
205
206impl_validate_list!(<T, S> std::collections::HashSet<T, S>);
207impl_validate_list!(<T> std::collections::BTreeSet<T>);
208impl_validate_list!(<T> std::collections::BinaryHeap<T>);
209impl_validate_list!(<T> std::collections::LinkedList<T>);
210impl_validate_list!(<T> std::collections::VecDeque<T>);
211impl_validate_list!(<T> std::vec::Vec<T>);
212impl_validate_list!(<T> [T]);
213
214impl<T: Validate, const N: usize> Validate for [T; N] {
215 type Context = T::Context;
216
217 fn validate_into(
218 &self,
219 ctx: &Self::Context,
220 mut parent: &mut dyn FnMut() -> Path,
221 report: &mut Report,
222 ) {
223 for (index, item) in self.iter().enumerate() {
224 let mut path = crate::util::nested_path!(parent, index);
225 <T as Validate>::validate_into(item, ctx, &mut path, report);
226 }
227 }
228}
229
230macro_rules! impl_validate_tuple {
231 ($A:ident, $($T:ident),*) => {
232 impl<$A, $($T),*> Validate for ($A, $($T,)*)
233 where
234 $A : Validate,
235 $($T : Validate<Context=$A::Context>,)*
236 {
237 type Context = $A::Context;
238
239 #[allow(non_snake_case)]
240 fn validate_into(&self, ctx: &Self::Context, mut parent: &mut dyn FnMut() -> Path, report: &mut Report) {
241 let ($A, $($T,)*) = self;
242 let mut index = 0usize;
243 let _index = index;
244 let mut path = $crate::util::nested_path!(parent, _index);
245 <$A as Validate>::validate_into($A, ctx, &mut path, report);
246 drop(path);
247 index += 1;
248 $({
249 let _index = index;
250 let mut path = $crate::util::nested_path!(parent, _index);
251 <$T as Validate>::validate_into($T, ctx, &mut path, report);
252 drop(path);
253 index += 1;
254 })*
255 let _ = index;
256 }
257 }
258 }
259}
260
261impl_validate_tuple!(A,);
262impl_validate_tuple!(A, B);
263impl_validate_tuple!(A, B, C);
264impl_validate_tuple!(A, B, C, D);
265impl_validate_tuple!(A, B, C, D, E);
266impl_validate_tuple!(A, B, C, D, E, F);
267impl_validate_tuple!(A, B, C, D, E, F, G);
268impl_validate_tuple!(A, B, C, D, E, F, G, H);
269impl_validate_tuple!(A, B, C, D, E, F, G, H, I);
270impl_validate_tuple!(A, B, C, D, E, F, G, H, I, J);
271impl_validate_tuple!(A, B, C, D, E, F, G, H, I, J, K);
272impl_validate_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
273
274impl Validate for () {
275 type Context = ();
276
277 fn validate_into(&self, _: &Self::Context, _: &mut dyn FnMut() -> Path, _: &mut Report) {}
278}
279
280impl<K, V, S> Validate for std::collections::HashMap<K, V, S>
281where
282 K: Clone + PathComponentKind,
283 V: Validate,
284{
285 type Context = V::Context;
286
287 fn validate_into(
288 &self,
289 ctx: &Self::Context,
290 mut parent: &mut dyn FnMut() -> Path,
291 report: &mut Report,
292 ) {
293 for (key, value) in self.iter() {
294 let mut path = crate::util::nested_path!(parent, key);
295 <V as Validate>::validate_into(value, ctx, &mut path, report);
296 }
297 }
298}
299
300impl<K, V> Validate for std::collections::BTreeMap<K, V>
301where
302 K: Clone + PathComponentKind,
303 V: Validate,
304{
305 type Context = V::Context;
306
307 fn validate_into(
308 &self,
309 ctx: &Self::Context,
310 mut parent: &mut dyn FnMut() -> Path,
311 report: &mut Report,
312 ) {
313 for (key, value) in self.iter() {
314 let mut path = crate::util::nested_path!(parent, key);
315 <V as Validate>::validate_into(value, ctx, &mut path, report);
316 }
317 }
318}
319
320impl<T: Validate> Validate for Option<T> {
321 type Context = T::Context;
322
323 fn validate_into(
324 &self,
325 ctx: &Self::Context,
326 parent: &mut dyn FnMut() -> Path,
327 report: &mut Report,
328 ) {
329 if let Some(value) = self {
330 value.validate_into(ctx, parent, report)
331 }
332 }
333}
334
335impl<B: Validate> Validate for std::borrow::Cow<'_, B>
336where
337 B: ToOwned,
338{
339 type Context = B::Context;
340
341 fn validate_into(
342 &self,
343 ctx: &Self::Context,
344 parent: &mut dyn FnMut() -> Path,
345 report: &mut Report,
346 ) {
347 self.as_ref().validate_into(ctx, parent, report)
348 }
349}