not_so_fast/
lib.rs

1//! A library for validating arbitrary data with simple API and a derive macro.
2//!
3//! ## Example
4//!
5//! ```
6//! use not_so_fast::{Validate, ValidationNode, ValidationError};
7//!
8//! #[derive(Validate)]
9//! struct User {
10//!     #[validate(custom = alpha_only, char_length(max = 30))]
11//!     nick: String,
12//!     #[validate(range(min = 15, max = 100))]
13//!     age: u8,
14//!     #[validate(length(max = 3), items(char_length(max = 50)))]
15//!     cars: Vec<String>,
16//! }
17//!
18//! fn alpha_only(s: &str) -> ValidationNode {
19//!     ValidationNode::error_if(
20//!         s.chars().any(|c| !c.is_alphanumeric()),
21//!         || ValidationError::with_code("alpha_only")
22//!     )
23//! }
24//!
25//! let user = User {
26//!     nick: "**tom1980**".into(),
27//!     age: 200,
28//!     cars: vec![
29//!         "first".into(),
30//!         "second".into(),
31//!         "third".repeat(11),
32//!         "fourth".into(),
33//!     ],
34//! };
35//!
36//! let node = user.validate();
37//! assert!(node.is_err());
38//! assert_eq!(
39//!     vec![
40//!         ".age: range: Number not in range: max=100, min=15, value=200",
41//!         ".cars: length: Invalid length: max=3, value=4",
42//!         ".cars[2]: char_length: Invalid character length: max=50, value=55",
43//!         ".nick: alpha_only",
44//!     ].join("\n"),
45//!     node.to_string()
46//! );
47//! ```
48
49use std::borrow::Cow;
50use std::collections::btree_map::Entry;
51use std::collections::BTreeMap;
52use std::fmt::Write;
53
54#[cfg(feature = "derive")]
55pub use not_so_fast_derive::Validate;
56
57/// Describes what is wrong with the validated value. It contains code, an
58/// optional message, and a list of error parameters.
59#[derive(Debug)]
60pub struct ValidationError {
61    /// Tells what feature of the validated value is not ok, e.g. "length",
62    /// "range", "invariant_xyz".
63    code: Cow<'static, str>,
64    /// Optional message explaining the error code, e.g. "Illegal array
65    /// length".
66    message: Option<Cow<'static, str>>,
67    /// A list of params that provide further context about the error, e.g. for
68    /// code "range": "min", "max", "value".
69    params: BTreeMap<Cow<'static, str>, ParamValue>,
70}
71
72impl ValidationError {
73    /// Creates an error with the provided code. Message and params are
74    /// initially empty.
75    /// ```
76    /// # use not_so_fast::*;
77    /// let error = ValidationError::with_code("length");
78    /// ```
79    pub fn with_code(code: impl Into<Cow<'static, str>>) -> Self {
80        Self {
81            code: code.into(),
82            message: None,
83            params: BTreeMap::new(),
84        }
85    }
86
87    /// Adds a message to the error. If called multiple times, the last message
88    /// will be preserved.
89    /// ```
90    /// # use not_so_fast::*;
91    /// let error = ValidationError::with_code("length").and_message("String too long");
92    /// ```
93    pub fn and_message(mut self, message: impl Into<Cow<'static, str>>) -> Self {
94        self.message = Some(message.into());
95        self
96    }
97
98    /// Adds a parameter to the error. If the same parameter (meaning keys are
99    /// equal) is added multiple times, the last value will be preserved.
100    /// ```
101    /// # use not_so_fast::*;
102    /// let error = ValidationError::with_code("length").and_param("max", 100);
103    /// ```
104    pub fn and_param(
105        mut self,
106        key: impl Into<Cow<'static, str>>,
107        value: impl Into<ParamValue>,
108    ) -> Self {
109        self.params.insert(key.into(), value.into());
110        self
111    }
112}
113
114/// Parameter value stored in [ValidationError].
115#[derive(Debug)]
116pub enum ParamValue {
117    Bool(bool),
118    I8(i8),
119    I16(i16),
120    I32(i32),
121    I64(i64),
122    I128(i128),
123    U8(u8),
124    U16(u16),
125    U32(u32),
126    U64(u64),
127    U128(u128),
128    Usize(usize),
129    F32(f32),
130    F64(f64),
131    Char(char),
132    String(Cow<'static, str>),
133    Raw(Cow<'static, str>),
134}
135
136impl std::fmt::Display for ParamValue {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        use ParamValue::*;
139        match self {
140            Bool(value) => write!(f, "{}", value),
141            I8(value) => write!(f, "{}", value),
142            I16(value) => write!(f, "{}", value),
143            I32(value) => write!(f, "{}", value),
144            I64(value) => write!(f, "{}", value),
145            I128(value) => write!(f, "{}", value),
146            U8(value) => write!(f, "{}", value),
147            U16(value) => write!(f, "{}", value),
148            U32(value) => write!(f, "{}", value),
149            U64(value) => write!(f, "{}", value),
150            U128(value) => write!(f, "{}", value),
151            Usize(value) => write!(f, "{}", value),
152            F32(value) => write!(f, "{}", value),
153            F64(value) => write!(f, "{}", value),
154            Char(value) => write!(f, "'{}'", value.escape_default()),
155            String(value) => write!(f, "\"{}\"", value.escape_default()),
156            Raw(value) => write!(f, "{}", value),
157        }
158    }
159}
160
161macro_rules! impl_param_conversion {
162    ($ty:ty, $variant:ident) => {
163        impl From<$ty> for ParamValue {
164            fn from(value: $ty) -> Self {
165                Self::$variant(value)
166            }
167        }
168    };
169}
170
171impl_param_conversion!(bool, Bool);
172impl_param_conversion!(i8, I8);
173impl_param_conversion!(i16, I16);
174impl_param_conversion!(i32, I32);
175impl_param_conversion!(i64, I64);
176impl_param_conversion!(i128, I128);
177impl_param_conversion!(u8, U8);
178impl_param_conversion!(u16, U16);
179impl_param_conversion!(u32, U32);
180impl_param_conversion!(u64, U64);
181impl_param_conversion!(u128, U128);
182impl_param_conversion!(usize, Usize);
183impl_param_conversion!(f32, F32);
184impl_param_conversion!(f64, F64);
185impl_param_conversion!(char, Char);
186
187impl From<&'static str> for ParamValue {
188    fn from(value: &'static str) -> Self {
189        Self::String(Cow::Borrowed(value))
190    }
191}
192
193impl From<String> for ParamValue {
194    fn from(value: String) -> Self {
195        Self::String(Cow::Owned(value))
196    }
197}
198
199/// Container for [ValidationError]s associated with some value. If the value
200/// is an object or a list, field or item ValidationNodes can be attached to
201/// the root node, effectively forming an error tree.
202#[derive(Debug)]
203pub struct ValidationNode {
204    /// Errors of the validated value.
205    errors: Vec<ValidationError>,
206    /// Errors of fields of the validated object.
207    fields: BTreeMap<Cow<'static, str>, ValidationNode>,
208    /// Errors of items of the validate list.
209    items: BTreeMap<usize, ValidationNode>,
210}
211
212impl ValidationNode {
213    /// Creates `ValidationNode` with no value errors, no field errors and no
214    /// item errors. You'll be able to add errors to the returned value later.
215    /// ```
216    /// # use not_so_fast::*;
217    /// let errors = ValidationNode::ok();
218    /// assert!(errors.is_ok());
219    /// assert_eq!("", errors.to_string());
220    /// ```
221    pub fn ok() -> Self {
222        Self {
223            errors: Default::default(),
224            fields: Default::default(),
225            items: Default::default(),
226        }
227    }
228
229    /// Converts `ValidationNode` into `Result<(), ValidationNode>`. It's
230    /// useful when you want to propagate errors with `?` operator or transform
231    /// the error using `Result`'s methods.
232    /// Returns `Ok(())` if `self` has no value errors, no field errors and no
233    /// item errors. Otherwise, returns `Err(self)`.
234    /// ```
235    /// # use not_so_fast::*;
236    /// let errors_ok = ValidationNode::ok();
237    /// assert!(matches!(errors_ok.result(), Ok(_)));
238    ///
239    /// let errors_bad = ValidationNode::error(ValidationError::with_code("abc"));
240    /// assert!(matches!(errors_bad.result(), Err(_)));
241    /// ```
242    pub fn result(self) -> Result<(), Self> {
243        if self.is_ok() {
244            Ok(())
245        } else {
246            Err(self)
247        }
248    }
249
250    /// Checks if `ValidationNode` has no value errors, no field errors, and
251    /// no item errors.
252    /// ```
253    /// # use not_so_fast::*;
254    /// let errors_ok = ValidationNode::ok();
255    /// assert!(errors_ok.is_ok());
256    ///
257    /// let errors_bad = ValidationNode::error(ValidationError::with_code("abc"));
258    /// assert!(!errors_bad.is_ok());
259    /// ```
260    pub fn is_ok(&self) -> bool {
261        self.errors.is_empty() && self.fields.is_empty() && self.items.is_empty()
262    }
263
264    /// Checks if `ValidationNode` has at least one value error, field error, or
265    /// item error.
266    /// ```
267    /// # use not_so_fast::*;
268    /// let errors_bad = ValidationNode::error(ValidationError::with_code("abc"));
269    /// assert!(errors_bad.is_err());
270    ///
271    /// let errors_ok = ValidationNode::ok();
272    /// assert!(!errors_ok.is_err());
273    /// ```
274    pub fn is_err(&self) -> bool {
275        !self.is_ok()
276    }
277
278    /// Recursively adds errors from `other` to `self`.
279    /// ```
280    /// # use not_so_fast::*;
281    /// let errors_a = ValidationNode::field("a", ValidationNode::error(ValidationError::with_code("123")));
282    /// let errors_b = ValidationNode::field("b", ValidationNode::error(ValidationError::with_code("456")));
283    /// let errors_c = errors_a.merge(errors_b);
284    /// assert!(errors_c.is_err());
285    /// assert_eq!(".a: 123\n.b: 456", errors_c.to_string());
286    /// ```
287    pub fn merge(mut self, other: Self) -> Self {
288        self.merge_in_place(other);
289        self
290    }
291
292    /// Merges `other` info `self` in-place (through `&mut`).
293    fn merge_in_place(&mut self, other: ValidationNode) {
294        self.errors.extend(other.errors);
295        for (key, value) in other.fields {
296            match self.fields.entry(key) {
297                Entry::Vacant(entry) => {
298                    entry.insert(value);
299                }
300                Entry::Occupied(mut entry) => {
301                    entry.get_mut().merge_in_place(value);
302                }
303            }
304        }
305        for (key, value) in other.items {
306            match self.items.entry(key) {
307                Entry::Vacant(entry) => {
308                    entry.insert(value);
309                }
310                Entry::Occupied(mut entry) => {
311                    entry.get_mut().merge_in_place(value);
312                }
313            }
314        }
315    }
316
317    /// Constructs `ValidationError` with one value error.
318    /// ```
319    /// # use not_so_fast::*;
320    /// let errors = ValidationNode::error(ValidationError::with_code("abc"));
321    /// assert!(errors.is_err());
322    /// assert_eq!(".: abc", errors.to_string());
323    /// ```
324    pub fn error(error: ValidationError) -> Self {
325        Self {
326            errors: vec![error],
327            fields: Default::default(),
328            items: Default::default(),
329        }
330    }
331
332    /// Adds one value error to `self`.
333    /// ```
334    /// # use not_so_fast::*;
335    /// let errors = ValidationNode::ok().and_error(ValidationError::with_code("abc"));
336    /// assert!(errors.is_err());
337    /// assert_eq!(".: abc", errors.to_string());
338    /// ```
339    pub fn and_error(mut self, error: ValidationError) -> Self {
340        self.errors.push(error);
341        self
342    }
343
344    /// Constructs `ValidationNode` with the value error returned by function
345    /// `f` if `condition` is `true`. Otherwise, returns
346    /// `ValidationNode::ok()`. Function `f` will be called at most once.
347    /// ```
348    /// # use not_so_fast::*;
349    /// let value = 10;
350    /// let errors = ValidationNode::error_if(value >= 20, || ValidationError::with_code("abc"));
351    /// assert!(errors.is_ok());
352    ///
353    /// let errors = ValidationNode::error_if(value >= 10, || ValidationError::with_code("def"));
354    /// assert!(errors.is_err());
355    /// assert_eq!(".: def", errors.to_string());
356    /// ```
357    pub fn error_if(condition: bool, f: impl FnOnce() -> ValidationError) -> Self {
358        Self {
359            errors: if condition {
360                vec![f()]
361            } else {
362                Default::default()
363            },
364            fields: Default::default(),
365            items: Default::default(),
366        }
367    }
368
369    /// Adds value error returned by function `f` to `ValidationNode` if
370    /// `condition` is `true`. Otherwise, returns unchanged `self`. Function
371    /// `f` will be called at most once.
372    /// ```
373    /// # use not_so_fast::*;
374    /// let value = 10;
375    /// let errors = ValidationNode::ok().and_error_if(value >= 20, || ValidationError::with_code("abc"));
376    /// assert!(errors.is_ok());
377    ///
378    /// let errors = ValidationNode::ok().and_error_if(value >= 10, || ValidationError::with_code("def"));
379    /// assert!(errors.is_err());
380    /// assert_eq!(".: def", errors.to_string());
381    /// ```
382    pub fn and_error_if(mut self, condition: bool, f: impl FnOnce() -> ValidationError) -> Self {
383        if condition {
384            self.errors.push(f());
385        }
386        self
387    }
388
389    /// Constructs `ValidationNode` from the value error iterator.
390    /// ```
391    /// # use not_so_fast::*;
392    /// let value = 9;
393    ///
394    /// // error if value is divisible by 3, 5, or 15
395    /// let errors_iter = [3, 5, 15]
396    ///     .into_iter()
397    ///     .filter_map(|divisor| (value % divisor == 0).then(|| {
398    ///         ValidationError::with_code("divisible").and_param("by", divisor)
399    ///     }));
400    ///
401    /// let errors = ValidationNode::errors(errors_iter);
402    /// assert!(errors.is_err());
403    /// assert_eq!(".: divisible: by=3", errors.to_string());
404    /// ```
405    pub fn errors(errors: impl Iterator<Item = ValidationError>) -> ValidationNode {
406        Self {
407            errors: errors.collect(),
408            fields: Default::default(),
409            items: Default::default(),
410        }
411    }
412
413    /// Adds value errors from `errors` iterator to `self`.
414    /// ```
415    /// # use not_so_fast::*;
416    /// let value = 9;
417    ///
418    /// // error if value is divisible by 3, 5, or 15
419    /// let errors_iter = [3, 5, 15]
420    ///     .into_iter()
421    ///     .filter_map(|divisor| (value % divisor == 0).then(|| {
422    ///         ValidationError::with_code("divisible").and_param("by", divisor)
423    ///     }));
424    ///
425    /// let errors = ValidationNode::ok().and_errors(errors_iter);
426    /// assert!(errors.is_err());
427    /// assert_eq!(".: divisible: by=3", errors.to_string());
428    /// ```
429    pub fn and_errors(mut self, errors: impl Iterator<Item = ValidationError>) -> ValidationNode {
430        self.errors.extend(errors);
431        self
432    }
433
434    /// Constructs `ValidationNode` with errors of one field. If
435    /// `validation_errors` is ok, the function also returns an ok node.
436    /// ```
437    /// # use not_so_fast::*;
438    /// let errors = ValidationNode::field("a", ValidationNode::ok());
439    /// assert!(errors.is_ok());
440    ///
441    /// let errors = ValidationNode::field("a", ValidationNode::error(ValidationError::with_code("abc")));
442    /// assert!(errors.is_err());
443    /// assert_eq!(".a: abc", errors.to_string());
444    /// ```
445    pub fn field(name: impl Into<Cow<'static, str>>, validation_errors: ValidationNode) -> Self {
446        Self {
447            errors: Default::default(),
448            fields: if !validation_errors.is_ok() {
449                let mut fields = BTreeMap::default();
450                fields.insert(name.into(), validation_errors);
451                fields
452            } else {
453                Default::default()
454            },
455            items: Default::default(),
456        }
457    }
458
459    /// Adds errors of one field to self. If self already contains errors for
460    /// that field, the errors will be merged. If `validation_errors` is ok,
461    /// the function will return self unchanged.
462    /// ```
463    /// # use not_so_fast::*;
464    /// let errors = ValidationNode::ok().and_field("a", ValidationNode::ok());
465    /// assert!(errors.is_ok());
466    ///
467    /// let errors = ValidationNode::ok().and_field("a", ValidationNode::error(ValidationError::with_code("abc")));
468    /// assert!(errors.is_err());
469    ///
470    /// let errors = ValidationNode::ok()
471    ///     .and_field("a", ValidationNode::error(ValidationError::with_code("abc")))
472    ///     .and_field("a", ValidationNode::error(ValidationError::with_code("def")))
473    ///     .and_field("b", ValidationNode::error(ValidationError::with_code("ghi")));
474    /// assert!(errors.is_err());
475    /// assert_eq!(".a: abc\n.a: def\n.b: ghi", errors.to_string());
476    /// ```
477    pub fn and_field(
478        mut self,
479        name: impl Into<Cow<'static, str>>,
480        validation_errors: ValidationNode,
481    ) -> Self {
482        if !validation_errors.is_ok() {
483            match self.fields.entry(name.into()) {
484                Entry::Vacant(entry) => {
485                    entry.insert(validation_errors);
486                }
487                Entry::Occupied(mut entry) => entry.get_mut().merge_in_place(validation_errors),
488            }
489        }
490        self
491    }
492
493    /// Collects field errors from an iterator to (key, value) pairs and a
494    /// function transforming key and value references into validation errors.
495    /// ```
496    /// # use not_so_fast::*;
497    /// let map: std::collections::HashMap<String, u32> = [
498    ///     ("one".into(), 1),
499    ///     ("two".into(), 2),
500    ///     ("three".into(), 3),
501    /// ].into_iter().collect();
502    /// let errors = ValidationNode::fields(map.iter(), |_key, value| {
503    ///     ValidationNode::error_if(*value > 2, || ValidationError::with_code("abc"))
504    /// });
505    /// assert!(errors.is_err());
506    /// assert_eq!(".three: abc", errors.to_string());
507    /// ```
508    pub fn fields<'a, K: 'a, V: 'a>(
509        iterator: impl Iterator<Item = (&'a K, &'a V)>,
510        mut f: impl FnMut(&'a K, &'a V) -> ValidationNode,
511    ) -> Self
512    where
513        // The requirement for K here is not `impl Into<Cow<_, str>>` like in
514        // `field` or `and_field`. That's because this function is meant to be
515        // used with dynamic objects, like `HashMap`, whose keys might not
516        // implement `Into<Cow<_, str>>` (think i32, uuid::Uuid, etc.).
517        K: ToString,
518    {
519        iterator.fold(ValidationNode::ok(), |acc, (key, value)| {
520            let validation_errors = f(key, value);
521
522            // Generate key string only if value has errors.
523            if !validation_errors.is_ok() {
524                let key_owned = Cow::Owned(key.to_string());
525                acc.and_field(key_owned, validation_errors)
526            } else {
527                acc
528            }
529        })
530    }
531
532    /// Adds field errors collected the same way as in
533    /// [fields](ValidationNode::fields) method to self.
534    /// ```
535    /// # use not_so_fast::*;
536    /// let map: std::collections::HashMap<String, u32> = [
537    ///     ("one".into(), 1),
538    ///     ("two".into(), 2),
539    ///     ("three".into(), 3),
540    /// ].into_iter().collect();
541    /// let errors = ValidationNode::ok().and_fields(map.iter(), |_key, value| {
542    ///     ValidationNode::error_if(*value > 2, || ValidationError::with_code("abc"))
543    /// });
544    /// assert!(errors.is_err());
545    /// assert_eq!(".three: abc", errors.to_string());
546    /// ```
547    pub fn and_fields<'a, K: 'a, V: 'a>(
548        self,
549        iterator: impl Iterator<Item = (&'a K, &'a V)>,
550        f: impl FnMut(&'a K, &'a V) -> ValidationNode,
551    ) -> Self
552    where
553        K: ToString,
554    {
555        self.merge(Self::fields(iterator, f))
556    }
557
558    /// Constructs `ValidationNode` with errors of one item. If
559    /// `validation_errors` is ok, the function also returns an ok node.
560    /// ```
561    /// # use not_so_fast::*;
562    /// let errors = ValidationNode::item(5, ValidationNode::ok());
563    /// assert!(errors.is_ok());
564    ///
565    /// let errors = ValidationNode::item(5, ValidationNode::error(ValidationError::with_code("abc")));
566    /// assert!(errors.is_err());
567    /// assert_eq!(".[5]: abc", errors.to_string());
568    /// ```
569    pub fn item(index: usize, validation_errors: ValidationNode) -> Self {
570        Self {
571            errors: Default::default(),
572            fields: Default::default(),
573            items: if !validation_errors.is_ok() {
574                let mut items = BTreeMap::default();
575                items.insert(index, validation_errors);
576                items
577            } else {
578                Default::default()
579            },
580        }
581    }
582
583    /// Adds errors of one item to self. If self already contains errors for
584    /// that item, the errors will be merged. If `validation_errors` is ok,
585    /// the function will return self unchanged.
586    /// ```
587    /// # use not_so_fast::*;
588    /// let errors = ValidationNode::ok().and_item(5, ValidationNode::ok());
589    /// assert!(errors.is_ok());
590    ///
591    /// let errors = ValidationNode::ok().and_item(5, ValidationNode::error(ValidationError::with_code("abc")));
592    /// assert!(errors.is_err());
593    ///
594    /// let errors = ValidationNode::ok()
595    ///     .and_item(5, ValidationNode::error(ValidationError::with_code("abc")))
596    ///     .and_item(5, ValidationNode::error(ValidationError::with_code("def")))
597    ///     .and_item(8, ValidationNode::error(ValidationError::with_code("ghi")));
598    /// assert!(errors.is_err());
599    /// assert_eq!(".[5]: abc\n.[5]: def\n.[8]: ghi", errors.to_string());
600    /// ```
601    pub fn and_item(mut self, index: usize, validation_errors: ValidationNode) -> Self {
602        if !validation_errors.is_ok() {
603            match self.items.entry(index) {
604                Entry::Vacant(entry) => {
605                    entry.insert(validation_errors);
606                }
607                Entry::Occupied(mut entry) => entry.get_mut().merge_in_place(validation_errors),
608            }
609        }
610        self
611    }
612
613    /// Collects item errors from an iterator to (index, value) pairs and a
614    /// function transforming index and value references into validation
615    /// errors.
616    /// ```
617    /// # use not_so_fast::*;
618    /// let list: Vec<u32> = vec![10, 20, 30];
619    ///
620    /// let errors = ValidationNode::items(list.iter(), |_index, value| {
621    ///     ValidationNode::error_if(*value > 25, || ValidationError::with_code("abc"))
622    /// });
623    /// assert!(errors.is_err());
624    /// assert_eq!(".[2]: abc", errors.to_string());
625    /// ```
626    pub fn items<'a, T: 'a>(
627        items: impl Iterator<Item = &'a T>,
628        mut f: impl FnMut(usize, &'a T) -> ValidationNode,
629    ) -> Self {
630        items
631            .enumerate()
632            .fold(ValidationNode::ok(), |acc, (index, item)| {
633                acc.and_item(index, f(index, item))
634            })
635    }
636
637    /// Adds item errors collected the same way as in
638    /// [items](ValidationNode::items) method to self.
639    /// ```
640    /// # use not_so_fast::*;
641    /// let list = vec![10, 20, 30];
642    ///
643    /// let errors = ValidationNode::ok().and_items(list.iter(), |_index, value| {
644    ///     ValidationNode::error_if(*value > 25, || ValidationError::with_code("abc"))
645    /// });
646    /// assert!(errors.is_err());
647    /// assert_eq!(".[2]: abc", errors.to_string());
648    /// ```
649    pub fn and_items<'a, T: 'a>(
650        self,
651        items: impl Iterator<Item = &'a T>,
652        f: impl FnMut(usize, &'a T) -> ValidationNode,
653    ) -> Self {
654        self.merge(Self::items(items, f))
655    }
656
657    /// Returns [ValidationNode] with only the first error, or an ok node
658    /// it there are no errors.
659    /// ```
660    /// # use not_so_fast::*;
661    /// let errors = ValidationNode::ok()
662    ///     .and_field("a", ValidationNode::error(ValidationError::with_code("1")))
663    ///     .and_field("a", ValidationNode::error(ValidationError::with_code("2")))
664    ///     .and_field("b", ValidationNode::error(ValidationError::with_code("3")));
665    /// assert_eq!(".a: 1\n.a: 2\n.b: 3", errors.to_string());
666    ///
667    /// let first = errors.first();
668    /// assert_eq!(".a: 1", first.to_string());
669    /// ```
670    pub fn first(mut self) -> Self {
671        if !self.errors.is_empty() {
672            Self {
673                errors: vec![self.errors.remove(0)],
674                fields: Default::default(),
675                items: Default::default(),
676            }
677        } else if !self.fields.is_empty() {
678            Self {
679                errors: Default::default(),
680                fields: self
681                    .fields
682                    .into_iter()
683                    .map(|(key, errors)| (key, errors.first()))
684                    .take(1)
685                    .collect(),
686                items: Default::default(),
687            }
688        } else if !self.items.is_empty() {
689            Self {
690                errors: Default::default(),
691                fields: Default::default(),
692                items: self
693                    .items
694                    .into_iter()
695                    .map(|(index, errors)| (index, errors.first()))
696                    .take(1)
697                    .collect(),
698            }
699        } else {
700            Self::ok()
701        }
702    }
703}
704
705/// Trait describing types that can be validated without arguments. It is
706/// automatically implemented for all types that implement `ValidateArgs<Args=()>`.
707pub trait Validate {
708    fn validate(&self) -> ValidationNode;
709}
710
711/// Trait describing types that can be validated with arguments.
712pub trait ValidateArgs<'arg> {
713    type Args;
714    fn validate_args(&self, args: Self::Args) -> ValidationNode;
715}
716
717impl<'a, T> Validate for T
718where
719    T: ValidateArgs<'a, Args = ()>,
720{
721    fn validate(&self) -> ValidationNode {
722        self.validate_args(())
723    }
724}
725
726impl std::fmt::Display for ValidationNode {
727    /// Prints validation errors, one per line with `jq`-like path and an error
728    /// description.
729    /// ```text
730    /// .: invariant_x: property x is not greater than property y
731    /// .abc[4]: length: illegal string length: min=10, max=20, value=34
732    /// .def.ghi: test
733    /// ```
734    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
735        let mut path = Vec::new();
736        display_fmt(self, &mut path, &mut false, f)
737    }
738}
739
740enum PathElement<'a> {
741    Name(&'a str),
742    Index(usize),
743}
744
745fn display_fmt<'s, 'p>(
746    node: &'s ValidationNode,
747    path: &'p mut Vec<PathElement<'s>>,
748    first_printed: &'p mut bool,
749    f: &mut std::fmt::Formatter,
750) -> std::fmt::Result {
751    for direct in node.errors.iter() {
752        if *first_printed {
753            f.write_char('\n')?;
754            fmt_path(path.as_slice(), f)?;
755            f.write_str(": ")?;
756            fmt_error(direct, f)?;
757        } else {
758            fmt_path(path.as_slice(), f)?;
759            f.write_str(": ")?;
760            fmt_error(direct, f)?;
761            *first_printed = true;
762        }
763    }
764    for field in node.fields.iter() {
765        path.push(PathElement::Name(field.0));
766        display_fmt(field.1, path, first_printed, f)?;
767        path.pop();
768    }
769    for item in node.items.iter() {
770        path.push(PathElement::Index(*item.0));
771        display_fmt(item.1, path, first_printed, f)?;
772        path.pop();
773    }
774    Ok(())
775}
776
777fn fmt_path(path: &[PathElement], f: &mut std::fmt::Formatter) -> std::fmt::Result {
778    if path.is_empty() {
779        return f.write_char('.');
780    }
781    for (i, element) in path.iter().enumerate() {
782        match element {
783            PathElement::Name(_) => {
784                f.write_char('.')?;
785                fmt_path_element(element, f)?;
786            }
787            PathElement::Index(_) => {
788                if i == 0 {
789                    f.write_char('.')?;
790                }
791                fmt_path_element(element, f)?;
792            }
793        }
794    }
795    Ok(())
796}
797
798fn fmt_path_element(element: &PathElement, f: &mut std::fmt::Formatter) -> std::fmt::Result {
799    match element {
800        PathElement::Name(name) => {
801            if !name.is_empty() && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
802                f.write_str(name)?;
803            } else {
804                f.write_char('"')?;
805                for c in name.chars() {
806                    if c == '"' {
807                        f.write_str("\\\"")?;
808                    } else {
809                        f.write_char(c)?;
810                    }
811                }
812                f.write_char('"')?;
813            }
814        }
815        PathElement::Index(index) => {
816            write!(f, "[{}]", index)?;
817        }
818    }
819    Ok(())
820}
821
822fn fmt_error(error: &ValidationError, f: &mut std::fmt::Formatter) -> std::fmt::Result {
823    f.write_str(error.code.as_ref())?;
824    if let Some(message) = &error.message {
825        f.write_str(": ")?;
826        f.write_str(message.as_ref())?;
827    }
828    for (i, param) in error.params.iter().enumerate() {
829        if i != 0 {
830            f.write_str(", ")?;
831        } else {
832            f.write_str(": ")?;
833        }
834        f.write_str(param.0)?;
835        f.write_str("=")?;
836        write!(f, "{}", param.1)?;
837    }
838    Ok(())
839}
840
841#[cfg(feature = "serde")]
842mod serde {
843    use std::fmt::Write;
844
845    use super::{ValidationError, ValidationNode};
846
847    impl serde::Serialize for ValidationNode {
848        /// Serializes validation node into a tree reflecting the structure
849        /// of validated data. Value errors are serialized as string lists
850        /// attached to validation nodes.
851        ///
852        /// ```json
853        /// {
854        ///     "errors": [
855        ///         "invariant_x: property x is not greater than property y"
856        ///     ],
857        ///     "abc": {
858        ///         "4": {
859        ///             "errors": "length: illegal string length: min=10, max=20, value=34"
860        ///         }
861        ///     },
862        ///     "def": {
863        ///         "ghi": {
864        ///             "errors": "test"
865        ///         }
866        ///     }
867        /// }
868        /// ```
869        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
870            // Every value error has to be rendered to a nice message string.
871            // To reduce the number of allocations, we crete a buffer at the
872            // root for the entire tree and reuse it. Unfortunately, serde does
873            // not allow passing mutable data down to serializers, so we'll
874            // pass mutable pointer and cast it to mut reference with unsafe.
875            let mut buffer = String::new();
876            SerializableValidationNode(self, &mut buffer).serialize(serializer)
877        }
878    }
879
880    struct SerializableValidationNode<'a>(&'a ValidationNode, *mut String);
881
882    impl<'a> serde::Serialize for SerializableValidationNode<'a> {
883        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
884            use serde::ser::SerializeMap;
885
886            let (node, buffer) = (self.0, self.1);
887
888            let entries =
889                usize::from(!node.errors.is_empty()) + node.fields.len() + node.items.len();
890
891            let mut map = serializer.serialize_map(Some(entries))?;
892
893            if !node.errors.is_empty() {
894                map.serialize_entry(
895                    "errors",
896                    &SerializableValidationErrors(&node.errors, buffer),
897                )?;
898            }
899            for (name, field) in &node.fields {
900                map.serialize_entry(name, &SerializableValidationNode(field, buffer))?;
901            }
902            for (index, item) in &node.items {
903                map.serialize_entry(index, &SerializableValidationNode(item, buffer))?;
904            }
905
906            map.end()
907        }
908    }
909
910    struct SerializableValidationErrors<'a>(&'a [ValidationError], *mut String);
911
912    impl<'a> serde::Serialize for SerializableValidationErrors<'a> {
913        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
914            use serde::ser::SerializeSeq;
915
916            let (errors, buffer) = (self.0, self.1);
917
918            let mut seq = serializer.serialize_seq(Some(errors.len()))?;
919
920            for error in errors {
921                seq.serialize_element(&SerializableValidationError(error, buffer))?;
922            }
923
924            seq.end()
925        }
926    }
927
928    struct SerializableValidationError<'a>(&'a ValidationError, *mut String);
929
930    impl<'a> serde::Serialize for SerializableValidationError<'a> {
931        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
932            let (error, buffer) = (self.0, self.1);
933
934            // This is a workaround for serde's serialization API giving us
935            // only immutable data. I can't think of any case where this could
936            // lead to undefined behavior.
937            let buffer = unsafe { buffer.as_mut().unwrap() };
938
939            buffer.write_str(&error.code).unwrap();
940
941            if let Some(message) = &error.message {
942                buffer.write_str(": ").unwrap();
943                buffer.write_str(message).unwrap();
944            }
945
946            for (i, param) in error.params.iter().enumerate() {
947                if i == 0 {
948                    buffer.write_str(": ").unwrap();
949                } else {
950                    buffer.write_str(", ").unwrap();
951                }
952                buffer.write_str(param.0).unwrap();
953                buffer.write_char('=').unwrap();
954                write!(buffer, "{}", param.1).unwrap();
955            }
956
957            let result = serializer.serialize_str(buffer);
958            buffer.clear();
959            result
960        }
961    }
962}