1use lazy_regex::{Lazy, Regex};
2use roadblk_attr::{
3 ConstraintType, EnumError, LengthError, ModelValidatorError, RangeError, RegexError,
4};
5use std::{
6 collections::{HashMap, HashSet},
7 fmt::Display,
8};
9
10pub trait Validator<Data> {
11 type Error;
12
13 fn validate(data: &Data) -> Result<(), Self::Error>;
15
16 fn constraint_type() -> ConstraintType;
18}
19
20impl<D, T: Validator<D>> Validator<Option<D>> for T {
21 type Error = T::Error;
22
23 fn validate(data: &Option<D>) -> Result<(), Self::Error> {
24 if let Some(data) = data {
25 return T::validate(data);
26 }
27 Ok(())
28 }
29
30 fn constraint_type() -> ConstraintType {
31 T::constraint_type()
32 }
33}
34
35pub trait RegexValidator<V> {
36 fn validate(regex: &Lazy<Regex>, value: &V) -> Result<(), RegexError>;
37}
38
39impl RegexValidator<String> for String {
40 fn validate(regex: &Lazy<Regex>, value: &String) -> Result<(), RegexError> {
41 if regex.is_match(value) {
42 return Ok(());
43 }
44 Err(RegexError {
45 regex: regex.to_string(),
46 value: value.to_string(),
47 })
48 }
49}
50
51impl RegexValidator<Option<String>> for Option<String> {
52 fn validate(regex: &Lazy<Regex>, value: &Option<String>) -> Result<(), RegexError> {
53 if let Some(value) = value {
54 return <String as RegexValidator<String>>::validate(regex, value);
55 }
56 Ok(())
57 }
58}
59
60pub trait RangeValidator {
61 type O: PartialOrd;
62 fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError>;
63}
64
65impl<D: NumberTrait + PartialOrd + Display> RangeValidator for D {
66 type O = D;
67
68 fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError> {
69 if *self < min {
70 return Err(RangeError {
71 min: format!("{min}"),
72 max: format!("{max}"),
73 value: format!("{self}"),
74 });
75 }
76 if *self > max {
77 return Err(RangeError {
78 min: format!("{min}"),
79 max: format!("{max}"),
80 value: format!("{self}"),
81 });
82 }
83 Ok(())
84 }
85}
86
87pub trait NumberTrait {}
88
89impl NumberTrait for i8 {}
90impl NumberTrait for i16 {}
91impl NumberTrait for i32 {}
92impl NumberTrait for i64 {}
93impl NumberTrait for i128 {}
94impl NumberTrait for u8 {}
95impl NumberTrait for usize {}
96impl NumberTrait for u16 {}
97impl NumberTrait for u32 {}
98impl NumberTrait for u64 {}
99impl NumberTrait for u128 {}
100impl NumberTrait for f32 {}
101impl NumberTrait for f64 {}
102
103impl<D: NumberTrait + PartialOrd + Display> RangeValidator for Option<D> {
104 type O = D;
105
106 fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError> {
107 if let Some(data) = self {
108 if *data < min {
109 return Err(RangeError {
110 min: format!("{min}"),
111 max: format!("{max}"),
112 value: format!("{data}"),
113 });
114 }
115 if *data > max {
116 return Err(RangeError {
117 min: format!("{min}"),
118 max: format!("{max}"),
119 value: format!("{data}"),
120 });
121 }
122 }
123 Ok(())
124 }
125}
126
127pub trait SelfValidator {
129 fn validate(&self) -> Result<(), ModelValidatorError>;
131}
132
133impl<T: SelfValidator> SelfValidator for Option<T> {
134 fn validate(&self) -> Result<(), ModelValidatorError> {
135 if let Some(data) = self {
136 return data.validate();
137 }
138 Ok(())
139 }
140}
141
142pub trait EnumValidator<'a, T> {
143 fn validate(data: &'a Self) -> Result<(), EnumError>;
144}
145
146impl<'a, T: TryFrom<&'a i32>> EnumValidator<'a, T> for i32 {
147 fn validate(data: &'a Self) -> Result<(), EnumError> {
148 if T::try_from(data).is_err() {
149 return Err(EnumError);
150 }
151 Ok(())
152 }
153}
154
155impl<'a, T: TryFrom<&'a i32>> EnumValidator<'a, T> for Option<i32> {
156 fn validate(data: &'a Self) -> Result<(), EnumError> {
157 if let Some(data) = data {
158 if T::try_from(data).is_err() {
159 return Err(EnumError);
160 }
161 }
162 Ok(())
163 }
164}
165
166impl<'a, T: TryFrom<&'a String>> EnumValidator<'a, T> for String {
167 fn validate(data: &'a Self) -> Result<(), EnumError> {
168 if T::try_from(data).is_err() {
169 return Err(EnumError);
170 }
171 Ok(())
172 }
173}
174
175impl<'a, T: TryFrom<&'a String>> EnumValidator<'a, T> for Option<String> {
176 fn validate(data: &'a Self) -> Result<(), EnumError> {
177 if let Some(data) = data {
178 if T::try_from(data).is_err() {
179 return Err(EnumError);
180 }
181 }
182 Ok(())
183 }
184}
185
186pub trait HasLength {
187 fn length(&self) -> usize;
188}
189
190impl HasLength for String {
191 fn length(&self) -> usize {
192 self.len()
193 }
194}
195
196impl<T> HasLength for Vec<T> {
197 fn length(&self) -> usize {
198 self.len()
199 }
200}
201
202impl<T> HasLength for HashSet<T> {
203 fn length(&self) -> usize {
204 self.len()
205 }
206}
207
208impl<K, V> HasLength for HashMap<K, V> {
209 fn length(&self) -> usize {
210 self.len()
211 }
212}
213
214pub struct LengthValidator<const MIN: usize, const MAX: usize>;
215
216impl<T: HasLength, const MIN: usize, const MAX: usize> Validator<T> for LengthValidator<MIN, MAX> {
217 type Error = LengthError;
218
219 fn validate(data: &T) -> Result<(), Self::Error> {
220 let len = data.length();
221 if len < MIN {
222 return Err(LengthError {
223 min: MIN,
224 max: MAX,
225 len,
226 });
227 }
228 if data.length() > MAX {
229 return Err(LengthError {
230 min: MIN,
231 max: MAX,
232 len,
233 });
234 }
235 Ok(())
236 }
237
238 fn constraint_type() -> ConstraintType {
239 ConstraintType::Length(MIN, MAX)
240 }
241}