form_validation/
validator_fn.rs

1use crate::{Validation, ValidationErrors};
2use std::{fmt::Debug, rc::Rc};
3use uuid::Uuid;
4
5#[cfg(feature = "async")]
6use std::{future::Future, marker::PhantomData, pin::Pin};
7
8type ValidatorFnTraitObject<Value, Key> = dyn Fn(&Value, &Key) -> Result<(), ValidationErrors<Key>>;
9
10/// Function to perform validation on a form field.
11///
12/// ## Example
13///
14/// ```
15/// use form_validation::{Validation, ValidationError, ValidatorFn};
16///
17/// let v: ValidatorFn<i32, String> = ValidatorFn::new(|value, key: &String| {
18///     if value < &0 {
19///         let value_clone = *value;
20///         Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
21///                 .with_message(move |key| {
22///                     format!(
23///                         "The value of {} ({}) cannot be less than 0",
24///                         key, value_clone
25///                     )
26///         }).into()) // convert into ValidationErrors
27///     } else {
28///         Ok(())
29///     }
30/// });
31///
32/// let key = "field1".to_string();
33/// assert!(v.validate_value(&20, &key).is_ok());
34/// let errors = v.validate_value(&-1, &key).unwrap_err();
35/// assert_eq!(1, errors.len());
36/// let error = errors.errors.get(0).unwrap();
37/// assert_eq!(
38///     "The value of field1 (-1) cannot be less than 0",
39///     error.to_string()
40/// );
41/// assert_eq!("NOT_LESS_THAN_0", error.type_id);
42/// ```
43pub struct ValidatorFn<Value, Key> {
44    closure: Rc<ValidatorFnTraitObject<Value, Key>>,
45    id: Uuid,
46}
47
48impl<Value, Key> ValidatorFn<Value, Key> {
49    /// Create a new `ValidatorFn`.
50    pub fn new<C>(closure: C) -> Self
51    where
52        C: Fn(&Value, &Key) -> Result<(), ValidationErrors<Key>> + 'static,
53    {
54        Self {
55            closure: Rc::new(closure),
56            id: Uuid::new_v4(),
57        }
58    }
59}
60
61impl<Value, Key> Clone for ValidatorFn<Value, Key> {
62    fn clone(&self) -> Self {
63        Self {
64            closure: Rc::clone(&self.closure),
65            id: self.id,
66        }
67    }
68}
69
70impl<Value, Key> PartialEq for ValidatorFn<Value, Key> {
71    fn eq(&self, other: &Self) -> bool {
72        self.id == other.id
73    }
74}
75
76impl<C, Value, Key> From<C> for ValidatorFn<Value, Key>
77where
78    C: Fn(&Value, &Key) -> Result<(), ValidationErrors<Key>> + 'static,
79{
80    fn from(closure: C) -> Self {
81        ValidatorFn::new(closure)
82    }
83}
84
85impl<Value, Key> Debug for ValidatorFn<Value, Key> {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(
88            f,
89            "ValidatorFn(closure: {:p}, id: {})",
90            self.closure, self.id
91        )
92    }
93}
94
95impl<Value, Key> Validation<Value, Key> for ValidatorFn<Value, Key>
96where
97    Key: Clone + PartialEq,
98{
99    fn validate_value(&self, value: &Value, key: &Key) -> Result<(), ValidationErrors<Key>> {
100        (self.closure)(value, key)
101    }
102}
103
104/// An function to perform validation on a field asynchonously.
105///
106/// For the synchronous version, see [ValidatorFn].
107///
108/// ## Example
109///
110/// ```
111/// use form_validation::{AsyncValidatorFn, ValidationError};
112/// use futures::executor::block_on;
113///
114/// let v: AsyncValidatorFn<i32, String> =
115///     AsyncValidatorFn::new(|value: &i32, key: &String| {
116///         let key = key.clone();
117///         let value = *value;
118///         Box::pin(async move {
119///             // perform actions here that require async
120///             if value < 0 {
121///                 Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
122///                     .with_message(move |key| {
123///                         format!(
124///                             "The value of {} ({}) cannot be less than 0",
125///                             key, value
126///                         )
127///                     })
128///                     .into()) // convert into ValidationErrors
129///             } else {
130///                 Ok(())
131///             }
132///         })
133///     });
134///
135/// let key = "field1".to_string();
136/// assert!(block_on(v.validate_value(&20, &key)).is_ok());
137///
138/// let errors = block_on(v.validate_value(&-1, &key)).unwrap_err();
139/// assert_eq!(1, errors.len());
140/// let error = errors.errors.get(0).unwrap();
141/// assert_eq!(
142///     "The value of field1 (-1) cannot be less than 0",
143///     error.to_string()
144/// );
145/// assert_eq!("NOT_LESS_THAN_0", error.type_id);
146/// ```
147#[cfg(feature = "async")]
148#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
149pub struct AsyncValidatorFn<Value, Key> {
150    future_producer: Rc<
151        dyn Fn(&Value, &Key) -> Pin<Box<dyn Future<Output = Result<(), ValidationErrors<Key>>>>>,
152    >,
153    id: Uuid,
154    key_type: PhantomData<Key>,
155    value_type: PhantomData<Value>,
156}
157
158#[cfg(feature = "async")]
159impl<Value, Key> AsyncValidatorFn<Value, Key>
160where
161    Key: Clone + PartialEq,
162    Value: Clone + PartialEq,
163{
164    /// Takes a closure that produces a `Future` that produces a
165    /// [ValidatorFn] closure.
166    pub fn new<C>(closure: C) -> Self
167    where
168        C: Fn(&Value, &Key) -> Pin<Box<dyn Future<Output = Result<(), ValidationErrors<Key>>>>>
169            + 'static,
170    {
171        Self {
172            future_producer: Rc::new(closure),
173            id: Uuid::new_v4(),
174            key_type: PhantomData,
175            value_type: PhantomData,
176        }
177    }
178
179    /// Runs the future to produce the [ValidatorFn] closure, and then
180    /// performs the validation with that.
181    pub async fn validate_value(
182        &self,
183        value: &Value,
184        key: &Key,
185    ) -> Result<(), ValidationErrors<Key>> {
186        let future = (self.future_producer)(value, key);
187        future.await
188    }
189}
190
191#[cfg(feature = "async")]
192impl<Value, Key> From<ValidatorFn<Value, Key>> for AsyncValidatorFn<Value, Key>
193where
194    Key: Clone + PartialEq + 'static,
195    Value: Clone + PartialEq + 'static,
196{
197    fn from(validator_fn: ValidatorFn<Value, Key>) -> Self {
198        Self::new(move |value, key| {
199            let value_clone = value.clone();
200            let key_clone = key.clone();
201            let new_fn = validator_fn.clone();
202            Box::pin(async move { new_fn.validate_value(&value_clone, &key_clone) })
203        })
204    }
205}
206
207#[cfg(feature = "async")]
208impl<Value, Key> PartialEq for AsyncValidatorFn<Value, Key> {
209    fn eq(&self, other: &Self) -> bool {
210        self.id == other.id
211    }
212}
213
214#[cfg(feature = "async")]
215impl<Value, Key> Clone for AsyncValidatorFn<Value, Key> {
216    fn clone(&self) -> Self {
217        Self {
218            future_producer: Rc::clone(&self.future_producer),
219            id: self.id,
220            key_type: PhantomData,
221            value_type: PhantomData,
222        }
223    }
224}
225
226#[cfg(feature = "async")]
227impl<Value, Key> Debug for AsyncValidatorFn<Value, Key> {
228    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229        write!(
230            f,
231            "AsyncValidatorFn(future_producer: {:p}, id: {})",
232            self.future_producer, self.id
233        )
234    }
235}