serde_valid/validation/generic/
enumerate.rs

1use crate::validation::ValidateCompositedEnumerate;
2use crate::EnumerateError;
3
4/// Enumerate validation.
5///
6/// See <https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values>
7///
8/// ```rust
9/// use serde_json::json;
10/// use serde_valid::{Validate, ValidateEnumerate};
11///
12/// struct MyType(String);
13///
14/// impl ValidateEnumerate<&'static str> for MyType {
15///     fn validate_enumerate(
16///         &self,
17///         enumerate: &[&'static str],
18///     ) -> Result<(), serde_valid::EnumerateError> {
19///         self.0.validate_enumerate(enumerate)
20///     }
21/// }
22///
23/// #[derive(Validate)]
24/// struct TestStruct {
25///     #[validate(enumerate = ["1", "2", "3"])]
26///     val: MyType,
27/// }
28///
29/// let s = TestStruct {
30///     val: MyType("4".to_string()),
31/// };
32///
33/// assert_eq!(
34///     s.validate().unwrap_err().to_string(),
35///     json!({
36///         "errors": [],
37///         "properties": {
38///             "val": {
39///                 "errors": ["The value must be in [1, 2, 3]."]
40///             }
41///         }
42///     })
43///     .to_string()
44/// );
45/// ```
46pub trait ValidateEnumerate<T> {
47    fn validate_enumerate(&self, enumerate: &[T]) -> Result<(), EnumerateError>;
48}
49
50macro_rules! impl_validate_generic_enumerate_literal {
51    ($type:ty) => {
52        impl ValidateEnumerate<$type> for $type {
53            fn validate_enumerate(&self, enumerate: &[$type]) -> Result<(), EnumerateError> {
54                if enumerate.iter().any(|candidate| candidate == self) {
55                    Ok(())
56                } else {
57                    Err(EnumerateError::new(enumerate))
58                }
59            }
60        }
61
62        impl<T> ValidateCompositedEnumerate<&[$type]> for T
63        where
64            T: ValidateEnumerate<$type>,
65        {
66            fn validate_composited_enumerate(
67                &self,
68                limit: &[$type],
69            ) -> Result<(), crate::validation::Composited<EnumerateError>> {
70                self.validate_enumerate(limit)
71                    .map_err(|error| crate::validation::Composited::Single(error))
72            }
73        }
74    };
75}
76
77impl_validate_generic_enumerate_literal!(i8);
78impl_validate_generic_enumerate_literal!(i16);
79impl_validate_generic_enumerate_literal!(i32);
80impl_validate_generic_enumerate_literal!(i64);
81#[cfg(feature = "i128")]
82impl_validate_generic_enumerate_literal!(i128);
83impl_validate_generic_enumerate_literal!(isize);
84impl_validate_generic_enumerate_literal!(u8);
85impl_validate_generic_enumerate_literal!(u16);
86impl_validate_generic_enumerate_literal!(u32);
87impl_validate_generic_enumerate_literal!(u64);
88#[cfg(feature = "i128")]
89impl_validate_generic_enumerate_literal!(u128);
90impl_validate_generic_enumerate_literal!(usize);
91impl_validate_generic_enumerate_literal!(std::num::NonZeroI8);
92impl_validate_generic_enumerate_literal!(std::num::NonZeroI16);
93impl_validate_generic_enumerate_literal!(std::num::NonZeroI32);
94impl_validate_generic_enumerate_literal!(std::num::NonZeroI64);
95#[cfg(feature = "i128")]
96impl_validate_generic_enumerate_literal!(std::num::NonZeroI128);
97impl_validate_generic_enumerate_literal!(std::num::NonZeroIsize);
98impl_validate_generic_enumerate_literal!(std::num::NonZeroU8);
99impl_validate_generic_enumerate_literal!(std::num::NonZeroU16);
100impl_validate_generic_enumerate_literal!(std::num::NonZeroU32);
101impl_validate_generic_enumerate_literal!(std::num::NonZeroU64);
102#[cfg(feature = "i128")]
103impl_validate_generic_enumerate_literal!(std::num::NonZeroU128);
104impl_validate_generic_enumerate_literal!(std::num::NonZeroUsize);
105impl_validate_generic_enumerate_literal!(f32);
106impl_validate_generic_enumerate_literal!(f64);
107impl_validate_generic_enumerate_literal!(char);
108
109macro_rules! impl_validate_generic_enumerate_str {
110    ($type:ty) => {
111        impl ValidateEnumerate<&'static str> for $type {
112            fn validate_enumerate(&self, enumerate: &[&'static str]) -> Result<(), EnumerateError> {
113                if enumerate.iter().any(|candidate| candidate == self) {
114                    Ok(())
115                } else {
116                    Err(EnumerateError::new(enumerate))
117                }
118            }
119        }
120    };
121}
122
123impl_validate_generic_enumerate_str!(&str);
124impl_validate_generic_enumerate_str!(String);
125impl_validate_generic_enumerate_str!(std::borrow::Cow<'_, str>);
126impl_validate_generic_enumerate_str!(&std::ffi::OsStr);
127impl_validate_generic_enumerate_str!(std::ffi::OsString);
128
129macro_rules! impl_validate_generic_enumerate_path {
130    ($type:ty) => {
131        impl ValidateEnumerate<&'static str> for $type {
132            fn validate_enumerate(&self, enumerate: &[&'static str]) -> Result<(), EnumerateError> {
133                if enumerate
134                    .iter()
135                    .any(|candidate| &std::path::Path::new(candidate) == self)
136                {
137                    Ok(())
138                } else {
139                    Err(EnumerateError::new(enumerate))
140                }
141            }
142        }
143    };
144}
145
146impl_validate_generic_enumerate_path!(&std::path::Path);
147impl_validate_generic_enumerate_path!(std::path::PathBuf);
148
149impl<T> ValidateCompositedEnumerate<&[&'static str]> for T
150where
151    T: ValidateEnumerate<&'static str>,
152{
153    fn validate_composited_enumerate(
154        &self,
155        limit: &[&'static str],
156    ) -> Result<(), crate::validation::Composited<EnumerateError>> {
157        self.validate_enumerate(limit)
158            .map_err(crate::validation::Composited::Single)
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_validate_integer_vec_type_is_true() {
168        assert!(ValidateEnumerate::validate_enumerate(&1, &[1, 2, 3]).is_ok());
169    }
170
171    #[test]
172    fn test_validate_integer_vec_type_is_false() {
173        assert!(ValidateEnumerate::validate_enumerate(&1, &[2, 3, 4]).is_err());
174    }
175
176    #[test]
177    fn test_validate_float_type_is_true() {
178        assert!(ValidateEnumerate::validate_enumerate(&0.9, &[0.9, 2.3, -3.0]).is_ok());
179    }
180
181    #[test]
182    fn test_validate_float_type_is_false() {
183        assert!(ValidateEnumerate::validate_enumerate(&0.9, &[0.8, 2.3, -3.0]).is_err());
184    }
185
186    #[test]
187    fn test_validate_unsigned_int_type() {
188        assert!(ValidateEnumerate::validate_enumerate(&1, &[-1, 0, 1, 2, 3]).is_ok());
189    }
190
191    #[test]
192    fn test_validate_str_type() {
193        assert!(ValidateEnumerate::validate_enumerate(&'a', &['a', 'b', 'c']).is_ok());
194    }
195
196    #[test]
197    fn test_validate_string_type() {
198        assert!(ValidateEnumerate::validate_enumerate(&'a', &['a', 'b', 'c']).is_ok());
199    }
200
201    #[test]
202    fn test_validate_os_str_type() {
203        assert!(ValidateEnumerate::validate_enumerate(
204            &std::ffi::OsStr::new("a"),
205            &["a", "b", "c"]
206        )
207        .is_ok());
208    }
209
210    #[test]
211    fn test_validate_os_string_type() {
212        assert!(ValidateEnumerate::validate_enumerate(
213            &std::ffi::OsString::from("a"),
214            &["a", "b", "c"]
215        )
216        .is_ok());
217    }
218
219    #[test]
220    fn test_validate_path_type() {
221        assert!(ValidateEnumerate::validate_enumerate(
222            &std::path::Path::new("a"),
223            &["a", "b", "c"]
224        )
225        .is_ok());
226    }
227
228    #[test]
229    fn test_validate_path_buf_type() {
230        assert!(ValidateEnumerate::validate_enumerate(
231            &std::path::PathBuf::from("a"),
232            &["a", "b", "c"]
233        )
234        .is_ok());
235    }
236}