icydb_base/validator/
num.rs

1use crate::{
2    core::traits::{NumCast, Validator},
3    prelude::*,
4};
5
6fn cast_decimal<N: NumCast + Clone>(value: &N) -> Result<Decimal, String> {
7    <Decimal as NumCast>::from(value.clone()).ok_or_else(|| {
8        format!(
9            "value of type {} cannot be represented as Decimal",
10            core::any::type_name::<N>()
11        )
12    })
13}
14
15///
16/// Lt
17///
18
19#[validator]
20pub struct Lt {
21    target: Decimal,
22}
23
24impl Lt {
25    pub fn new<N: NumCast + Clone>(target: N) -> Self {
26        let target = cast_decimal(&target)
27            .unwrap_or_else(|e| panic!("Lt::new failed to convert target: {e}"));
28
29        Self { target }
30    }
31}
32
33impl<N: NumCast + Clone> Validator<N> for Lt {
34    fn validate(&self, value: &N) -> Result<(), String> {
35        let v = cast_decimal(value)?;
36
37        if v < self.target {
38            Ok(())
39        } else {
40            Err(format!("{} must be < {}", v, self.target))
41        }
42    }
43}
44
45///
46/// Gt
47///
48
49#[validator]
50pub struct Gt {
51    target: Decimal,
52}
53
54impl Gt {
55    pub fn new<N: NumCast + Clone>(target: N) -> Self {
56        let target = cast_decimal(&target)
57            .unwrap_or_else(|e| panic!("Gt::new failed to convert target: {e}"));
58
59        Self { target }
60    }
61}
62
63impl<N: NumCast + Clone> Validator<N> for Gt {
64    fn validate(&self, value: &N) -> Result<(), String> {
65        let v = cast_decimal(value)?;
66
67        if v > self.target {
68            Ok(())
69        } else {
70            Err(format!("{} must be > {}", v, self.target))
71        }
72    }
73}
74
75///
76/// Lte
77///
78
79#[validator]
80pub struct Lte {
81    target: Decimal,
82}
83
84impl Lte {
85    pub fn new<N: NumCast + Clone>(target: N) -> Self {
86        let target = cast_decimal(&target)
87            .unwrap_or_else(|e| panic!("Lte::new failed to convert target: {e}"));
88
89        Self { target }
90    }
91}
92
93impl<N: NumCast + Clone> Validator<N> for Lte {
94    fn validate(&self, value: &N) -> Result<(), String> {
95        let v = cast_decimal(value)?;
96
97        if v <= self.target {
98            Ok(())
99        } else {
100            Err(format!("{} must be <= {}", v, self.target))
101        }
102    }
103}
104
105///
106/// Gte
107///
108
109#[validator]
110pub struct Gte {
111    target: Decimal,
112}
113
114impl Gte {
115    pub fn new<N: NumCast + Clone>(target: N) -> Self {
116        let target = cast_decimal(&target)
117            .unwrap_or_else(|e| panic!("Gte::new failed to convert target: {e}"));
118
119        Self { target }
120    }
121}
122
123impl<N: NumCast + Clone> Validator<N> for Gte {
124    fn validate(&self, value: &N) -> Result<(), String> {
125        let v = cast_decimal(value)?;
126
127        if v >= self.target {
128            Ok(())
129        } else {
130            Err(format!("{} must be >= {}", v, self.target))
131        }
132    }
133}
134
135///
136/// Equal
137///
138
139#[validator]
140pub struct Equal {
141    target: Decimal,
142}
143
144impl Equal {
145    pub fn new<N: NumCast + Clone>(target: N) -> Self {
146        let target = cast_decimal(&target)
147            .unwrap_or_else(|e| panic!("Equal::new failed to convert target: {e}"));
148
149        Self { target }
150    }
151}
152
153impl<N: NumCast + Clone> Validator<N> for Equal {
154    fn validate(&self, value: &N) -> Result<(), String> {
155        let v = cast_decimal(value)?;
156
157        if v == self.target {
158            Ok(())
159        } else {
160            Err(format!("{} must be == {}", v, self.target))
161        }
162    }
163}
164
165///
166/// NotEqual
167///
168
169#[validator]
170pub struct NotEqual {
171    target: Decimal,
172}
173
174impl NotEqual {
175    pub fn new<N: NumCast + Clone>(target: N) -> Self {
176        let target = cast_decimal(&target)
177            .unwrap_or_else(|e| panic!("NotEqual::new failed to convert target: {e}"));
178
179        Self { target }
180    }
181}
182
183impl<N: NumCast + Clone> Validator<N> for NotEqual {
184    fn validate(&self, value: &N) -> Result<(), String> {
185        let v = cast_decimal(value)?;
186
187        if v == self.target {
188            Err(format!("{} must be != {}", v, self.target))
189        } else {
190            Ok(())
191        }
192    }
193}
194
195///
196/// Range
197///
198
199#[validator]
200pub struct Range {
201    min: Decimal,
202    max: Decimal,
203}
204
205impl Range {
206    pub fn new<N: NumCast + Clone>(min: N, max: N) -> Self {
207        let min =
208            cast_decimal(&min).unwrap_or_else(|e| panic!("Range::new failed to convert min: {e}"));
209        let max =
210            cast_decimal(&max).unwrap_or_else(|e| panic!("Range::new failed to convert max: {e}"));
211        assert!(min <= max, "range requires min <= max");
212
213        Self { min, max }
214    }
215}
216
217impl<N: NumCast + Clone> Validator<N> for Range {
218    fn validate(&self, n: &N) -> Result<(), String> {
219        let v = cast_decimal(n)?;
220
221        if v < self.min || v > self.max {
222            Err(format!(
223                "{} must be between {} and {}",
224                v, self.min, self.max
225            ))
226        } else {
227            Ok(())
228        }
229    }
230}
231
232///
233/// MultipleOf
234///
235
236#[validator]
237pub struct MultipleOf {
238    target: Decimal,
239}
240
241impl MultipleOf {
242    pub fn new<N: NumCast + Clone>(target: N) -> Self {
243        let target = cast_decimal(&target)
244            .unwrap_or_else(|e| panic!("MultipleOf::new failed to convert target: {e}"));
245        Self { target }
246    }
247}
248
249impl<N: NumCast + Clone> Validator<N> for MultipleOf {
250    fn validate(&self, n: &N) -> Result<(), String> {
251        let v = cast_decimal(n)?;
252
253        if (*v % *self.target).is_zero() {
254            Ok(())
255        } else {
256            Err(format!("{v} is not a multiple of {}", self.target))
257        }
258    }
259}
260
261///
262/// TESTS
263///
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    fn dec(v: &str) -> Decimal {
270        Decimal::from_str(v).unwrap()
271    }
272
273    // ---------------------
274    // Lt
275    // ---------------------
276
277    #[test]
278    fn lt_success() {
279        assert!(Lt::new(10).validate(&5).is_ok());
280        assert!(Lt::new(5.1).validate(&5.0).is_ok());
281        assert!(Lt::new(dec("10.0")).validate(&dec("9.999")).is_ok());
282    }
283
284    #[test]
285    fn lt_failure() {
286        assert!(Lt::new(5).validate(&5).is_err());
287        assert!(Lt::new(5).validate(&6).is_err());
288    }
289
290    // ---------------------
291    // Gt
292    // ---------------------
293
294    #[test]
295    fn gt_success() {
296        assert!(Gt::new(5).validate(&10).is_ok());
297        assert!(Gt::new(dec("1.0")).validate(&dec("1.0001")).is_ok());
298    }
299
300    #[test]
301    fn gt_failure() {
302        assert!(Gt::new(10).validate(&10).is_err());
303        assert!(Gt::new(10).validate(&5).is_err());
304    }
305
306    // ---------------------
307    // Lte
308    // ---------------------
309
310    #[test]
311    fn lte_success() {
312        assert!(Lte::new(5).validate(&5).is_ok());
313        assert!(Lte::new(5).validate(&4).is_ok());
314        assert!(Lte::new(dec("1.0")).validate(&dec("1.0")).is_ok());
315    }
316
317    #[test]
318    fn lte_failure() {
319        assert!(Lte::new(5).validate(&6).is_err());
320    }
321
322    // ---------------------
323    // Gte
324    // ---------------------
325
326    #[test]
327    fn gte_success() {
328        assert!(Gte::new(5).validate(&5).is_ok());
329        assert!(Gte::new(5).validate(&6).is_ok());
330    }
331
332    #[test]
333    fn gte_failure() {
334        assert!(Gte::new(5).validate(&4).is_err());
335    }
336
337    // ---------------------
338    // Equal
339    // ---------------------
340
341    #[test]
342    fn equal_success() {
343        assert!(Equal::new(5).validate(&5).is_ok());
344        assert!(Equal::new(dec("1.23")).validate(&dec("1.23")).is_ok());
345    }
346
347    #[test]
348    fn equal_failure() {
349        assert!(Equal::new(5).validate(&6).is_err());
350        assert!(Equal::new(dec("1.23")).validate(&dec("1.2300001")).is_err());
351    }
352
353    // ---------------------
354    // NotEqual
355    // ---------------------
356
357    #[test]
358    fn not_equal_success() {
359        assert!(NotEqual::new(5).validate(&6).is_ok());
360        assert!(NotEqual::new(dec("1.23")).validate(&dec("1.2301")).is_ok());
361    }
362
363    #[test]
364    fn not_equal_failure() {
365        assert!(NotEqual::new(5).validate(&5).is_err());
366        assert!(NotEqual::new(dec("1.23")).validate(&dec("1.23")).is_err());
367    }
368
369    // ---------------------
370    // Range
371    // ---------------------
372
373    #[test]
374    fn range_success() {
375        let r = Range::new(0, 10);
376        assert!(r.validate(&0).is_ok());
377        assert!(r.validate(&5).is_ok());
378        assert!(r.validate(&10).is_ok());
379
380        let r2 = Range::new(dec("1.23"), dec("2.34"));
381        assert!(r2.validate(&dec("1.23")).is_ok());
382        assert!(r2.validate(&dec("2.34")).is_ok());
383        assert!(r2.validate(&dec("1.5")).is_ok());
384    }
385
386    #[test]
387    fn range_failure() {
388        let r = Range::new(0, 10);
389        assert!(r.validate(&-1).is_err());
390        assert!(r.validate(&11).is_err());
391        assert!(r.validate(&dec("-0.0001")).is_err());
392    }
393
394    #[test]
395    fn range_min_equals_max() {
396        let r = Range::new(5, 5);
397        assert!(r.validate(&5).is_ok());
398        assert!(r.validate(&4).is_err());
399        assert!(r.validate(&6).is_err());
400    }
401
402    #[test]
403    #[should_panic(expected = "range requires min <= max")]
404    fn range_invalid_constructor() {
405        Range::new(10, 5);
406    }
407
408    // ---------------------
409    // MultipleOf
410    // ---------------------
411
412    #[test]
413    fn multiple_of_int_success() {
414        assert!(MultipleOf::new(5).validate(&10).is_ok());
415        assert!(MultipleOf::new(5).validate(&0).is_ok());
416    }
417
418    #[test]
419    fn multiple_of_int_failure() {
420        assert!(MultipleOf::new(5).validate(&11).is_err());
421        assert!(MultipleOf::new(3).validate(&10).is_err());
422    }
423
424    #[test]
425    fn multiple_of_decimal_success() {
426        assert!(MultipleOf::new(dec("0.5")).validate(&dec("2.5")).is_ok());
427        assert!(MultipleOf::new(dec("0.1")).validate(&dec("1.2")).is_ok());
428        assert!(MultipleOf::new(dec("1.25")).validate(&dec("6.25")).is_ok());
429    }
430
431    #[test]
432    fn multiple_of_decimal_failure() {
433        assert!(MultipleOf::new(dec("0.5")).validate(&dec("2.6")).is_err());
434        assert!(MultipleOf::new(dec("0.1")).validate(&dec("1.23")).is_err());
435    }
436
437    #[test]
438    fn multiple_of_zero_edge_case() {
439        // Depending on your intended semantics, this may be allowed or not.
440        // If target = 0 is illegal, this should panic during new().
441        assert!(MultipleOf::new(1).validate(&0).is_ok());
442    }
443}