skp_validator_rules/numeric/
multiple_of.rs1use skp_validator_core::{Rule, ValidationContext, ValidationErrors, ValidationError, ValidationResult};
4
5#[derive(Debug, Clone)]
20pub struct MultipleOfRule<T> {
21 pub value: T,
23 pub message: Option<String>,
25}
26
27impl<T> MultipleOfRule<T> {
28 pub fn new(value: T) -> Self {
30 Self {
31 value,
32 message: None,
33 }
34 }
35
36 pub fn message(mut self, msg: impl Into<String>) -> Self {
38 self.message = Some(msg.into());
39 self
40 }
41}
42
43impl MultipleOfRule<f64> {
44 fn get_message(&self) -> String {
45 self.message.clone().unwrap_or_else(|| {
46 format!("Must be a multiple of {}", self.value)
47 })
48 }
49}
50
51impl MultipleOfRule<i64> {
52 fn get_message(&self) -> String {
53 self.message.clone().unwrap_or_else(|| {
54 format!("Must be a multiple of {}", self.value)
55 })
56 }
57}
58
59impl Rule<f64> for MultipleOfRule<f64> {
60 fn validate(&self, value: &f64, _ctx: &ValidationContext) -> ValidationResult<()> {
61 if self.value == 0.0 {
62 return Ok(()); }
64
65 let remainder = value % self.value;
66 if remainder.abs() < f64::EPSILON || (self.value - remainder.abs()).abs() < f64::EPSILON {
68 Ok(())
69 } else {
70 Err(ValidationErrors::from_iter([
71 ValidationError::root("multiple_of", self.get_message())
72 .with_param("divisor", self.value)
73 ]))
74 }
75 }
76
77 fn name(&self) -> &'static str {
78 "multiple_of"
79 }
80
81 fn default_message(&self) -> String {
82 self.get_message()
83 }
84}
85
86impl Rule<f32> for MultipleOfRule<f64> {
87 fn validate(&self, value: &f32, ctx: &ValidationContext) -> ValidationResult<()> {
88 <Self as Rule<f64>>::validate(self, &(*value as f64), ctx)
89 }
90
91 fn name(&self) -> &'static str {
92 "multiple_of"
93 }
94
95 fn default_message(&self) -> String {
96 self.get_message()
97 }
98}
99
100impl Rule<i64> for MultipleOfRule<i64> {
101 fn validate(&self, value: &i64, _ctx: &ValidationContext) -> ValidationResult<()> {
102 if self.value == 0 {
103 return Ok(()); }
105
106 if value % self.value == 0 {
107 Ok(())
108 } else {
109 Err(ValidationErrors::from_iter([
110 ValidationError::root("multiple_of", self.get_message())
111 .with_param("divisor", self.value)
112 ]))
113 }
114 }
115
116 fn name(&self) -> &'static str {
117 "multiple_of"
118 }
119
120 fn default_message(&self) -> String {
121 self.get_message()
122 }
123}
124
125impl Rule<i32> for MultipleOfRule<i64> {
126 fn validate(&self, value: &i32, ctx: &ValidationContext) -> ValidationResult<()> {
127 <Self as Rule<i64>>::validate(self, &(*value as i64), ctx)
128 }
129
130 fn name(&self) -> &'static str {
131 "multiple_of"
132 }
133
134 fn default_message(&self) -> String {
135 self.get_message()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_multiple_of_float() {
145 let rule = MultipleOfRule::new(5.0);
146 let ctx = ValidationContext::default();
147
148 assert!(rule.validate(&10.0, &ctx).is_ok());
149 assert!(rule.validate(&15.0, &ctx).is_ok());
150 assert!(rule.validate(&7.0, &ctx).is_err());
151 }
152
153 #[test]
154 fn test_multiple_of_int() {
155 let rule = MultipleOfRule::new(3_i64);
156 let ctx = ValidationContext::default();
157
158 assert!(rule.validate(&9_i64, &ctx).is_ok());
159 assert!(rule.validate(&12_i64, &ctx).is_ok());
160 assert!(rule.validate(&7_i64, &ctx).is_err());
161 }
162
163 #[test]
164 fn test_custom_message() {
165 let rule = MultipleOfRule::new(10.0).message("Must be a multiple of 10");
166 assert_eq!(rule.get_message(), "Must be a multiple of 10");
167 }
168}