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}