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}