serde_valid/validation/numeric/
multiple_of.rs

1use crate::validation::{impl_generic_composited_validation_1args, ValidateCompositedMultipleOf};
2use crate::MultipleOfError;
3
4/// Multipl validation of the number.
5///
6/// See <https://json-schema.org/understanding-json-schema/reference/numeric.html#multiples>
7///
8/// ```rust
9/// use serde_json::json;
10/// use serde_valid::{Validate, ValidateMultipleOf};
11///
12/// struct MyType(i32);
13///
14/// impl ValidateMultipleOf<i32> for MyType {
15///     fn validate_multiple_of(
16///         &self,
17///         multiple_of: i32,
18///     ) -> Result<(), serde_valid::MultipleOfError> {
19///         self.0.validate_multiple_of(multiple_of)
20///     }
21/// }
22///
23/// #[derive(Validate)]
24/// struct TestStruct {
25///     #[validate(multiple_of = 5)]
26///     val: MyType,
27/// }
28///
29/// let s = TestStruct { val: MyType(6) };
30///
31/// assert_eq!(
32///     s.validate().unwrap_err().to_string(),
33///     json!({
34///         "errors": [],
35///         "properties": {
36///             "val": {
37///                 "errors": ["The value must be multiple of `5`."]
38///             }
39///         }
40///     })
41///     .to_string()
42/// );
43/// ```
44pub trait ValidateMultipleOf<T>
45where
46    T: std::cmp::PartialEq + std::ops::Rem<Output = T> + num_traits::Zero,
47{
48    fn validate_multiple_of(&self, multiple_of: T) -> Result<(), crate::MultipleOfError>;
49}
50
51macro_rules! impl_validate_numeric_multiple_of {
52    ($type:ty) => {
53        impl ValidateMultipleOf<$type> for $type {
54            fn validate_multiple_of(
55                &self,
56                multiple_of: $type,
57            ) -> Result<(), crate::MultipleOfError> {
58                if std::cmp::PartialEq::<$type>::eq(
59                    &(*self % multiple_of),
60                    &num_traits::Zero::zero(),
61                ) {
62                    Ok(())
63                } else {
64                    Err(crate::MultipleOfError::new(multiple_of))
65                }
66            }
67        }
68
69        impl_generic_composited_validation_1args!(MultipleOf, $type);
70    };
71}
72
73impl_validate_numeric_multiple_of!(i8);
74impl_validate_numeric_multiple_of!(i16);
75impl_validate_numeric_multiple_of!(i32);
76impl_validate_numeric_multiple_of!(i64);
77#[cfg(feature = "i128")]
78impl_validate_numeric_multiple_of!(i128);
79impl_validate_numeric_multiple_of!(isize);
80impl_validate_numeric_multiple_of!(u8);
81impl_validate_numeric_multiple_of!(u16);
82impl_validate_numeric_multiple_of!(u32);
83impl_validate_numeric_multiple_of!(u64);
84#[cfg(feature = "i128")]
85impl_validate_numeric_multiple_of!(u128);
86impl_validate_numeric_multiple_of!(usize);
87impl_validate_numeric_multiple_of!(f32);
88impl_validate_numeric_multiple_of!(f64);
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_validate_numeric_multiple_of_integer_is_true() {
96        assert!(ValidateMultipleOf::validate_multiple_of(&10i8, 5).is_ok());
97        assert!(ValidateMultipleOf::validate_multiple_of(&10i16, 5).is_ok());
98        assert!(ValidateMultipleOf::validate_multiple_of(&10i32, 5).is_ok());
99        assert!(ValidateMultipleOf::validate_multiple_of(&10i64, 5).is_ok());
100        assert!(ValidateMultipleOf::validate_multiple_of(&10isize, 5).is_ok());
101
102        assert!(ValidateMultipleOf::validate_multiple_of(&10u8, 5).is_ok());
103        assert!(ValidateMultipleOf::validate_multiple_of(&10u16, 5).is_ok());
104        assert!(ValidateMultipleOf::validate_multiple_of(&10u32, 5).is_ok());
105        assert!(ValidateMultipleOf::validate_multiple_of(&10u64, 5).is_ok());
106        assert!(ValidateMultipleOf::validate_multiple_of(&10usize, 5).is_ok());
107    }
108
109    #[test]
110    fn test_validate_numeric_multiple_of_integer_is_false() {
111        assert!(ValidateMultipleOf::validate_multiple_of(&10, 3).is_err());
112        assert!(ValidateMultipleOf::validate_multiple_of(&10i8, 3).is_err());
113        assert!(ValidateMultipleOf::validate_multiple_of(&10i16, 3).is_err());
114        assert!(ValidateMultipleOf::validate_multiple_of(&10i32, 3).is_err());
115        assert!(ValidateMultipleOf::validate_multiple_of(&10i64, 3).is_err());
116        assert!(ValidateMultipleOf::validate_multiple_of(&10isize, 3).is_err());
117
118        assert!(ValidateMultipleOf::validate_multiple_of(&10u8, 3).is_err());
119        assert!(ValidateMultipleOf::validate_multiple_of(&10u16, 3).is_err());
120        assert!(ValidateMultipleOf::validate_multiple_of(&10u32, 3).is_err());
121        assert!(ValidateMultipleOf::validate_multiple_of(&10u64, 3).is_err());
122        assert!(ValidateMultipleOf::validate_multiple_of(&10usize, 3).is_err());
123    }
124
125    #[test]
126    fn test_validate_numeric_multiple_of_float_is_true() {
127        assert!(ValidateMultipleOf::validate_multiple_of(&12.0, 1.0).is_ok());
128        assert!(ValidateMultipleOf::validate_multiple_of(&12.5, 0.5).is_ok());
129    }
130
131    #[test]
132    fn test_validate_numeric_multiple_of_float_is_false() {
133        assert!(ValidateMultipleOf::validate_multiple_of(&12.0, 5.0).is_err());
134        assert!(ValidateMultipleOf::validate_multiple_of(&12.5, 0.3).is_err());
135    }
136
137    #[test]
138    #[cfg(feature = "i128")]
139    fn test_validate_numeric_multiple_of_128bit_integer_is_true() {
140        assert!(ValidateMultipleOf::validate_multiple_of(&10i128, 5).is_ok());
141        assert!(ValidateMultipleOf::validate_multiple_of(&10u128, 5).is_ok());
142    }
143
144    #[test]
145    #[cfg(feature = "i128")]
146    fn test_validate_numeric_multiple_of_128bit_integer_is_false() {
147        assert!(ValidateMultipleOf::validate_multiple_of(&10i128, 3).is_err());
148        assert!(ValidateMultipleOf::validate_multiple_of(&10u128, 3).is_err());
149    }
150}