skp_validator_rules/numeric/
range.rs1use skp_validator_core::{Rule, ValidationContext, ValidationErrors, ValidationError, ValidationResult};
4
5#[derive(Debug, Clone)]
22pub struct RangeRule<T> {
23 pub min: Option<T>,
25 pub max: Option<T>,
27 pub exclusive_min: bool,
29 pub exclusive_max: bool,
31 pub message: Option<String>,
33}
34
35impl<T> RangeRule<T> {
36 pub fn new() -> Self {
38 Self {
39 min: None,
40 max: None,
41 exclusive_min: false,
42 exclusive_max: false,
43 message: None,
44 }
45 }
46
47 pub fn min(mut self, min: T) -> Self {
49 self.min = Some(min);
50 self
51 }
52
53 pub fn max(mut self, max: T) -> Self {
55 self.max = Some(max);
56 self
57 }
58
59 pub fn exclusive_min(mut self, min: T) -> Self {
61 self.min = Some(min);
62 self.exclusive_min = true;
63 self
64 }
65
66 pub fn exclusive_max(mut self, max: T) -> Self {
68 self.max = Some(max);
69 self.exclusive_max = true;
70 self
71 }
72
73 pub fn message(mut self, msg: impl Into<String>) -> Self {
75 self.message = Some(msg.into());
76 self
77 }
78}
79
80impl<T> Default for RangeRule<T> {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86macro_rules! impl_range_rule {
87 ($($t:ty),+) => {
88 $(
89 impl Rule<$t> for RangeRule<$t> {
90 fn validate(&self, value: &$t, _ctx: &ValidationContext) -> ValidationResult<()> {
91 if let Some(ref min) = self.min {
93 let failed = if self.exclusive_min {
94 value <= min
95 } else {
96 value < min
97 };
98
99 if failed {
100 let msg = self.message.clone().unwrap_or_else(|| {
101 if self.exclusive_min {
102 format!("Must be greater than {}", min)
103 } else {
104 format!("Must be at least {}", min)
105 }
106 });
107 return Err(ValidationErrors::from_iter([
108 ValidationError::root("range.min", msg)
109 ]));
110 }
111 }
112
113 if let Some(ref max) = self.max {
115 let failed = if self.exclusive_max {
116 value >= max
117 } else {
118 value > max
119 };
120
121 if failed {
122 let msg = self.message.clone().unwrap_or_else(|| {
123 if self.exclusive_max {
124 format!("Must be less than {}", max)
125 } else {
126 format!("Must be at most {}", max)
127 }
128 });
129 return Err(ValidationErrors::from_iter([
130 ValidationError::root("range.max", msg)
131 ]));
132 }
133 }
134
135 Ok(())
136 }
137
138 fn name(&self) -> &'static str {
139 "range"
140 }
141
142 fn default_message(&self) -> String {
143 "Value out of range".to_string()
144 }
145 }
146 )+
147 };
148}
149
150impl_range_rule!(i8, i16, i32, i64, i128, isize);
151impl_range_rule!(u8, u16, u32, u64, u128, usize);
152impl_range_rule!(f32, f64);
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_range_f64() {
160 let rule = RangeRule::<f64>::new().min(0.0).max(100.0);
161 let ctx = ValidationContext::default();
162
163 assert!(rule.validate(&0.0, &ctx).is_ok());
164 assert!(rule.validate(&50.0, &ctx).is_ok());
165 assert!(rule.validate(&100.0, &ctx).is_ok());
166 assert!(rule.validate(&-1.0, &ctx).is_err());
167 assert!(rule.validate(&101.0, &ctx).is_err());
168 }
169
170 #[test]
171 fn test_range_i32() {
172 let rule = RangeRule::<i32>::new().min(18).max(120);
173 let ctx = ValidationContext::default();
174
175 assert!(rule.validate(&18, &ctx).is_ok());
176 assert!(rule.validate(&30, &ctx).is_ok());
177 assert!(rule.validate(&17, &ctx).is_err());
178 assert!(rule.validate(&121, &ctx).is_err());
179 }
180
181 #[test]
182 fn test_exclusive() {
183 let rule = RangeRule::<i32>::new().exclusive_min(0).exclusive_max(10);
184 let ctx = ValidationContext::default();
185
186 assert!(rule.validate(&0, &ctx).is_err()); assert!(rule.validate(&1, &ctx).is_ok());
188 assert!(rule.validate(&9, &ctx).is_ok());
189 assert!(rule.validate(&10, &ctx).is_err()); }
191}