1use crate::error::ValidationError;
2use crate::format_helper::format_template;
3use crate::traits::Validator;
4use std::fmt::Display;
5use std::sync::Arc;
6
7type NumberRule<T> = Arc<dyn Fn(&T) -> Option<ValidationError> + Send + Sync>;
8
9#[derive(Clone)]
11pub struct NumberValidator<T> {
12 rules: Vec<NumberRule<T>>,
13}
14
15impl<T> Default for NumberValidator<T> {
16 fn default() -> Self {
17 Self { rules: Vec::new() }
18 }
19}
20
21impl<T> NumberValidator<T>
22where
23 T: PartialOrd + Copy + Display + Send + Sync + 'static,
24{
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn min(self, minimum: T) -> Self {
32 self.min_msg(minimum, "Must be at least {min}, got {value}")
33 }
34
35 pub fn min_msg(mut self, minimum: T, msg_template: &str) -> Self {
39 let tpl = msg_template.to_string();
40 self.rules.push(Arc::new(move |val| {
41 if *val < minimum {
42 let msg = format_template(
43 &tpl,
44 &[("min", &minimum.to_string()), ("value", &val.to_string())],
45 );
46 Some(ValidationError::new("min_value", msg))
47 } else {
48 None
49 }
50 }));
51 self
52 }
53
54 pub fn max(self, maximum: T) -> Self {
56 self.max_msg(maximum, "Must not exceed {max}, got {value}")
57 }
58
59 pub fn max_msg(mut self, maximum: T, msg_template: &str) -> Self {
63 let tpl = msg_template.to_string();
64 self.rules.push(Arc::new(move |val| {
65 if *val > maximum {
66 let msg = format_template(
67 &tpl,
68 &[("max", &maximum.to_string()), ("value", &val.to_string())],
69 );
70 Some(ValidationError::new("max_value", msg))
71 } else {
72 None
73 }
74 }));
75 self
76 }
77
78 pub fn range(self, min_val: T, max_val: T) -> Self {
80 self.range_msg(
81 min_val,
82 max_val,
83 "Must be between {min} and {max}, got {value}",
84 )
85 }
86
87 pub fn range_msg(mut self, min_val: T, max_val: T, msg_template: &str) -> Self {
91 let tpl = msg_template.to_string();
92 self.rules.push(Arc::new(move |val| {
93 if *val < min_val || *val > max_val {
94 let msg = format_template(
95 &tpl,
96 &[
97 ("min", &min_val.to_string()),
98 ("max", &max_val.to_string()),
99 ("value", &val.to_string()),
100 ],
101 );
102 Some(ValidationError::new("range", msg))
103 } else {
104 None
105 }
106 }));
107 self
108 }
109
110 pub fn custom<F>(mut self, validator: F) -> Self
112 where
113 F: Fn(&T) -> Result<(), ValidationError> + Send + Sync + 'static,
114 {
115 self.rules.push(Arc::new(move |val| validator(val).err()));
116 self
117 }
118}
119
120impl<T> NumberValidator<T>
121where
122 T: PartialOrd + Default + Copy + Display + Send + Sync + 'static,
123{
124 pub fn positive(self) -> Self {
126 self.positive_msg("Must be greater than 0, got {value}")
127 }
128
129 pub fn positive_msg(mut self, msg_template: &str) -> Self {
133 let zero = T::default();
134 let tpl = msg_template.to_string();
135 self.rules.push(Arc::new(move |val| {
136 if *val <= zero {
137 let msg = format_template(&tpl, &[("value", &val.to_string())]);
138 Some(ValidationError::new("positive", msg))
139 } else {
140 None
141 }
142 }));
143 self
144 }
145
146 pub fn negative(self) -> Self {
148 self.negative_msg("Must be less than 0, got {value}")
149 }
150
151 pub fn negative_msg(mut self, msg_template: &str) -> Self {
155 let zero = T::default();
156 let tpl = msg_template.to_string();
157 self.rules.push(Arc::new(move |val| {
158 if *val >= zero {
159 let msg = format_template(&tpl, &[("value", &val.to_string())]);
160 Some(ValidationError::new("negative", msg))
161 } else {
162 None
163 }
164 }));
165 self
166 }
167}
168
169impl<T: Send + Sync> Validator<T> for NumberValidator<T> {
170 fn validate_all(&self, value: &T) -> Vec<ValidationError> {
171 let mut errors = Vec::new();
172 for rule in &self.rules {
173 if let Some(err) = rule(value) {
174 errors.push(err);
175 }
176 }
177 errors
178 }
179}