Skip to main content

vld/combinators/
transform.rs

1use serde_json::Value;
2use std::marker::PhantomData;
3
4use crate::error::VldError;
5use crate::schema::VldSchema;
6
7/// Transforms the output of a schema after successful parsing.
8///
9/// Created via [`VldSchema::transform()`].
10///
11/// # Example
12/// ```
13/// use vld::prelude::*;
14///
15/// // Parse a string, then transform it to its length
16/// let len_schema = vld::string().transform(|s| s.len());
17/// ```
18pub struct ZTransform<T, F, U>
19where
20    T: VldSchema,
21    F: Fn(T::Output) -> U,
22{
23    inner: T,
24    transform_fn: F,
25    _phantom: PhantomData<U>,
26}
27
28impl<T, F, U> ZTransform<T, F, U>
29where
30    T: VldSchema,
31    F: Fn(T::Output) -> U,
32{
33    /// Create the schema (the same as its `vld::` constructor function).
34    pub fn new(inner: T, transform_fn: F) -> Self {
35        Self {
36            inner,
37            transform_fn,
38            _phantom: PhantomData,
39        }
40    }
41
42    /// Access the inner schema.
43    pub fn inner_schema(&self) -> &T {
44        &self.inner
45    }
46}
47
48impl<T, F, U> VldSchema for ZTransform<T, F, U>
49where
50    T: VldSchema,
51    F: Fn(T::Output) -> U,
52{
53    type Output = U;
54
55    fn parse_value(&self, value: &Value) -> Result<U, VldError> {
56        Ok((self.transform_fn)(self.inner.parse_value(value)?))
57    }
58    fn parse_missing(&self) -> Result<U, VldError> {
59        Ok((self.transform_fn)(self.inner.parse_missing()?))
60    }
61}
62
63/// Transforms the output with a fallible function: an `Err` becomes a
64/// validation issue (`IssueCode::Custom { code: "transform" }`, the error's
65/// text as the message).
66///
67/// Created via [`VldSchema::try_transform()`].
68///
69/// ```
70/// use vld::prelude::*;
71///
72/// let port = vld::string().try_transform(|s| s.parse::<u16>());
73/// assert_eq!(port.parse(r#""8080""#).unwrap(), 8080);
74/// let err = port.parse(r#""99999""#).unwrap_err();
75/// assert_eq!(err.issues[0].code.key(), "transform");
76/// assert_eq!(err.issues[0].message, "number too large to fit in target type");
77/// ```
78pub struct ZTryTransform<T, F, U> {
79    inner: T,
80    transform_fn: F,
81    _phantom: PhantomData<U>,
82}
83
84impl<T, F, U> ZTryTransform<T, F, U> {
85    /// Create the schema (the same as [`VldSchema::try_transform`]).
86    pub fn new(inner: T, transform_fn: F) -> Self {
87        Self {
88            inner,
89            transform_fn,
90            _phantom: PhantomData,
91        }
92    }
93
94    /// Access the inner schema.
95    pub fn inner_schema(&self) -> &T {
96        &self.inner
97    }
98}
99
100impl<T, F, U, E> ZTryTransform<T, F, U>
101where
102    T: VldSchema,
103    F: Fn(T::Output) -> Result<U, E>,
104    E: std::fmt::Display,
105{
106    fn apply(&self, out: T::Output) -> Result<U, VldError> {
107        (self.transform_fn)(out).map_err(|e| {
108            VldError::single(
109                crate::error::IssueCode::Custom {
110                    code: "transform".to_string(),
111                },
112                e.to_string(),
113            )
114        })
115    }
116}
117
118impl<T, F, U, E> VldSchema for ZTryTransform<T, F, U>
119where
120    T: VldSchema,
121    F: Fn(T::Output) -> Result<U, E>,
122    E: std::fmt::Display,
123{
124    type Output = U;
125
126    fn parse_value(&self, value: &Value) -> Result<U, VldError> {
127        self.apply(self.inner.parse_value(value)?)
128    }
129    fn parse_missing(&self) -> Result<U, VldError> {
130        self.apply(self.inner.parse_missing()?)
131    }
132}