validex/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4mod condition;
5mod len;
6mod number;
7
8/// Error types used by the validation checks.
9pub mod errors;
10
11pub use condition::*;
12pub use len::*;
13pub use number::*;
14pub use validex_macros::Check;
15
16/// A dynamic error type.
17pub type DynError<'err> = Box<dyn std::error::Error + Send + Sync + 'err>;
18
19/// A trait that powers conditional combinators [`Not`], [`All`], [`Any`]
20pub trait Verify<Args> {
21    /// The error type produced when verify fails.
22    type Error;
23
24    /// Verifies the given arguments.
25    fn verify(&self, _: Args) -> bool;
26
27    /// Produces an error for the given arguments.
28    fn error(&self, _: Args) -> Self::Error;
29}
30
31/// Trait for performing a check on a field.
32///
33/// This is the heart of the `validex` library.
34///
35/// Any types that implement [`Check`] trait can be used to validate fields using
36/// `#[check(...)]` attribute.
37///
38/// Every function `Fn(&T) -> Result<(), E>` automatically implements this trait.
39///
40/// ### Example
41///
42/// ```rust
43/// # use validex::{Check, Length};
44/// #[derive(Check)]
45/// struct Input {
46///     #[check(is_even)]
47///     id: i32,
48///     #[check(Length(3..=64), is_ascii)]
49///     name: String,
50/// }
51///
52/// fn is_even(&num: &i32) -> Result<(), String> {
53///     if num % 2 != 0 {
54///         return Err(format!("{} is not even", num));
55///     }
56///     Ok(())
57/// }
58///
59/// fn is_ascii(string: &dyn AsRef<str>) -> Result<(), String> {
60///     let name = string.as_ref();
61///     if !name.chars().all(|c| c.is_ascii()) {
62///         return Err(format!("{} contains non-ascii characters", name));
63///     }
64///     Ok(())
65/// }
66/// ```
67pub trait Check<Args> {
68    /// The type of error returned if the check fails.
69    type Error;
70
71    /// Performs the check on the given input.
72    fn check(&self, _: Args) -> Result<(), Self::Error>;
73}
74
75impl<F, T, E> Check<T> for F
76where
77    F: Fn(T) -> Result<(), E>,
78{
79    type Error = E;
80    #[inline]
81    fn check(&self, args: T) -> Result<(), Self::Error> {
82        self(args)
83    }
84}
85
86pub(crate) fn check<'a, T, V>(this: &V, val: &'a T) -> Result<(), V::Error>
87where
88    T: ?Sized,
89    V: Verify<&'a T>,
90{
91    if !this.verify(val) {
92        return Err(this.error(val));
93    }
94    Ok(())
95}
96
97#[doc(hidden)]
98pub fn __field<'e, V, T>(key: &'static str, this: &V, val: T) -> Result<(), errors::FieldError<'e>>
99where
100    V: Check<T>,
101    V::Error: Into<DynError<'e>>,
102{
103    Check::check(this, val).map_err(|err| errors::FieldError::new(key, err))
104}