Skip to main content

web_core_validator/
traits.rs

1use crate::types::{ValidationErrors, ValidationErrorsKind};
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::collections::btree_map::BTreeMap;
5use std::future::Future;
6
7/// This is the original trait that was implemented by deriving `Validate`. It will still be
8/// implemented for struct validations that don't take custom arguments. The call is being
9/// forwarded to the `ValidateArgs<'v_a>` trait.
10#[trait_variant::make(Send)]
11pub trait Validate {
12    fn validate(&self) -> impl Future<Output = Result<(), ValidationErrors>>;
13}
14
15impl<T: Validate + Send + Sync> Validate for &T {
16    async fn validate(&self) -> Result<(), ValidationErrors> {
17        T::validate(self).await
18    }
19}
20
21macro_rules! impl_validate_list {
22    ($container:ty) => {
23        impl<T: Validate + Send + Sync> Validate for $container
24        where
25            T: Send,
26        {
27            async fn validate(&self) -> Result<(), ValidationErrors> {
28                let mut vec_err: BTreeMap<usize, Box<ValidationErrors>> = BTreeMap::new();
29
30                for (index, item) in self.iter().enumerate() {
31                    if let Err(e) = item.validate().await {
32                        vec_err.insert(index, Box::new(e));
33                    }
34                }
35
36                if vec_err.is_empty() {
37                    Ok(())
38                } else {
39                    let err_kind = ValidationErrorsKind::List(vec_err);
40                    let errors = ValidationErrors(std::collections::HashMap::from([(
41                        Cow::Borrowed("_tmp_validator"),
42                        err_kind,
43                    )]));
44                    Err(errors)
45                }
46            }
47        }
48    };
49}
50
51impl_validate_list!(std::collections::HashSet<T>);
52impl_validate_list!(std::collections::BTreeSet<T>);
53impl_validate_list!(std::collections::BinaryHeap<T>);
54impl_validate_list!(std::collections::LinkedList<T>);
55impl_validate_list!(std::collections::VecDeque<T>);
56impl_validate_list!(std::vec::Vec<T>);
57impl_validate_list!([T]);
58
59impl<T: Validate + Send + Sync, const N: usize> Validate for [T; N]
60where
61    T: Send,
62{
63    async fn validate(&self) -> Result<(), ValidationErrors> {
64        let mut vec_err: BTreeMap<usize, Box<ValidationErrors>> = BTreeMap::new();
65
66        for (index, item) in self.iter().enumerate() {
67            if let Err(e) = item.validate().await {
68                vec_err.insert(index, Box::new(e));
69            }
70        }
71
72        if vec_err.is_empty() {
73            Ok(())
74        } else {
75            let err_kind = ValidationErrorsKind::List(vec_err);
76            let errors = ValidationErrors(std::collections::HashMap::from([(
77                Cow::Borrowed("_tmp_validator"),
78                err_kind,
79            )]));
80            Err(errors)
81        }
82    }
83}
84
85impl<K, V, S> Validate for HashMap<K, V, S>
86where
87    K: Send + Sync,
88    V: Validate + Send + Sync,
89    S: Send + Sync,
90{
91    async fn validate(&self) -> Result<(), ValidationErrors> {
92        let mut vec_err: BTreeMap<usize, Box<ValidationErrors>> = BTreeMap::new();
93
94        for (index, (_key, value)) in self.iter().enumerate() {
95            if let Err(e) = value.validate().await {
96                vec_err.insert(index, Box::new(e));
97            }
98        }
99
100        if vec_err.is_empty() {
101            Ok(())
102        } else {
103            let err_kind = ValidationErrorsKind::List(vec_err);
104            let errors =
105                ValidationErrors(HashMap::from([(Cow::Borrowed("_tmp_validator"), err_kind)]));
106            Err(errors)
107        }
108    }
109}
110
111impl<K, V> Validate for BTreeMap<K, V>
112where
113    K: Send + Sync,
114    V: Validate + Send + Sync,
115{
116    async fn validate(&self) -> Result<(), ValidationErrors> {
117        let mut vec_err: BTreeMap<usize, Box<ValidationErrors>> = BTreeMap::new();
118
119        for (index, (_key, value)) in self.iter().enumerate() {
120            if let Err(e) = value.validate().await {
121                vec_err.insert(index, Box::new(e));
122            }
123        }
124
125        if vec_err.is_empty() {
126            Ok(())
127        } else {
128            let err_kind = ValidationErrorsKind::List(vec_err);
129            let errors =
130                ValidationErrors(HashMap::from([(Cow::Borrowed("_tmp_validator"), err_kind)]));
131            Err(errors)
132        }
133    }
134}
135
136/// A marker trait to associate a validated type with its context/state type.
137/// This is used by `ValidatedJson` to ensure the type can be extracted from
138/// axum requests with the correct state type.
139pub trait HasContext<T> {}
140
141/// The `Args` type can use the lifetime `'v_a` to pass references onto the validator.
142#[trait_variant::make(Send)]
143pub trait ValidateArgs<'v_a> {
144    type Args;
145    fn validate_with_args(
146        &self,
147        args: &mut Self::Args,
148    ) -> impl Future<Output = Result<(), ValidationErrors>>;
149}
150
151impl<'v_a, T, U> ValidateArgs<'v_a> for Option<T>
152where
153    T: ValidateArgs<'v_a, Args = U>,
154    U: Send,
155    T: Sync,
156{
157    type Args = U;
158
159    async fn validate_with_args(&self, args: &mut Self::Args) -> Result<(), ValidationErrors> {
160        if let Some(nested) = self {
161            T::validate_with_args(nested, args).await
162        } else {
163            Ok(())
164        }
165    }
166}