valitron/rule/available/
range.rs

1//! Range validate rule, support `u8`, `u16`, `u32`, `u64`, `i8`,
2//! `i16`, `i32`, `i64`, `f32`, `f64` and char. other types always return false.
3//!
4//! # Examples
5//! ```
6//! # use serde::Serialize;
7//! # use valitron::{available::{Range, MessageKind}, Validatable, Validator};
8//! #[derive(Serialize, Debug)]
9//! struct Input {
10//!     num: u8,
11//! }
12//!
13//! let input = Input { num: 9 };
14//! let err = input
15//!     .validate(Validator::new().rule("num", Range::new(10_u8..20)))
16//!     .unwrap_err();
17//!
18//! assert!(matches!(
19//!     err.get("num").unwrap()[0].kind(),
20//!     MessageKind::Range
21//! ));
22//!
23//! let input = Input { num: 15 };
24//! input
25//!     .validate(Validator::new().rule("num", Range::new(10_u8..20)))
26//!     .unwrap();
27//! ```
28
29use std::{fmt::Debug, marker::PhantomData, ops::RangeBounds};
30
31use super::Message;
32use crate::{Rule, Value};
33
34#[derive(Clone)]
35pub struct Range<T, Num> {
36    value: T,
37    _marker: PhantomData<Num>,
38}
39
40impl<T: Debug, Num> Debug for Range<T, Num> {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        f.debug_struct("Range")
43            .field("value", &self.value)
44            .field("_marker", &format_args!("-"))
45            .finish()
46    }
47}
48
49impl<T: Copy, Num: Clone> Copy for Range<T, Num> {}
50
51const NAME: &str = "range";
52
53impl<T, Num> Range<T, Num> {
54    pub fn new(value: T) -> Self {
55        Self {
56            value,
57            _marker: PhantomData,
58        }
59    }
60
61    fn message_in(&self) -> Message {
62        Message::new(super::MessageKind::Range)
63    }
64}
65
66macro_rules! impl_range {
67    ($val:ident($ty:ty)) => {
68        impl<T> Rule for Range<T, $ty>
69        where
70            T: RangeBounds<$ty> + Clone,
71        {
72            type Message = Message;
73
74            const NAME: &'static str = NAME;
75
76            fn message(&self) -> Self::Message {
77                self.message_in()
78            }
79
80            fn call(&mut self, data: &mut Value) -> bool {
81                match data {
82                    Value::$val(n) => self.value.contains(n),
83                    _ => false,
84                }
85            }
86        }
87    };
88}
89
90impl_range!(Uint8(u8));
91impl_range!(Int8(i8));
92impl_range!(Uint16(u16));
93impl_range!(Int16(i16));
94impl_range!(Uint32(u32));
95impl_range!(Int32(i32));
96impl_range!(Uint64(u64));
97impl_range!(Int64(i64));
98impl_range!(Char(char));
99
100impl<T> Rule for Range<T, f32>
101where
102    T: RangeBounds<f32> + Clone + 'static,
103{
104    type Message = Message;
105
106    const NAME: &'static str = NAME;
107
108    fn message(&self) -> Self::Message {
109        self.message_in()
110    }
111
112    fn call(&mut self, data: &mut Value) -> bool {
113        match data {
114            Value::Float32(f) => self.value.contains(f.as_ref()),
115            _ => false,
116        }
117    }
118}
119
120impl<T> Rule for Range<T, f64>
121where
122    T: RangeBounds<f64> + Clone + 'static,
123{
124    type Message = Message;
125
126    const NAME: &'static str = NAME;
127
128    fn message(&self) -> Self::Message {
129        self.message_in()
130    }
131
132    fn call(&mut self, data: &mut Value) -> bool {
133        match data {
134            Value::Float64(f) => self.value.contains(f.as_ref()),
135            _ => false,
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use crate::{rule::IntoRuleList, RuleExt, ValueMap};
143
144    use super::{super::Required, Range};
145
146    fn register<R: IntoRuleList<ValueMap, M>, M>(_: R) {}
147
148    #[test]
149    fn test_register() {
150        register(Required.and(Range::new(1..10)));
151    }
152}