rustapi_validate/
custom.rs1use crate::error::{FieldError, ValidationError};
2use std::collections::HashMap;
3
4pub trait Rule<T: ?Sized> {
6 fn validate(&self, value: &T) -> Result<(), FieldError>;
8}
9
10type ValidationRuleFn<T> = Box<dyn Fn(&T) -> Result<(), FieldError> + Send + Sync>;
12
13pub struct Validator<T> {
15 rules: Vec<ValidationRuleFn<T>>,
16}
17
18impl<T> Default for Validator<T> {
19 fn default() -> Self {
20 Self { rules: Vec::new() }
21 }
22}
23
24impl<T> Validator<T> {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn rule<F>(mut self, rule: F) -> Self
32 where
33 F: Fn(&T) -> Result<(), FieldError> + Send + Sync + 'static,
34 {
35 self.rules.push(Box::new(rule));
36 self
37 }
38
39 pub fn validate(&self, value: &T) -> Result<(), ValidationError> {
41 let mut errors = Vec::new();
42
43 for rule in &self.rules {
44 if let Err(e) = rule(value) {
45 errors.push(e);
46 }
47 }
48
49 if errors.is_empty() {
50 Ok(())
51 } else {
52 Err(ValidationError::new(errors))
53 }
54 }
55}
56
57pub mod rules {
59 use super::*;
60
61 pub fn required<T: AsRef<str>>(
63 message: impl Into<String>,
64 ) -> impl Fn(&Option<T>) -> Result<(), FieldError> {
65 let message = message.into();
66 move |value: &Option<T>| match value {
67 Some(v) if !v.as_ref().is_empty() => Ok(()),
68 _ => Err(FieldError::new("required", "required", message.clone())),
69 }
70 }
71
72 pub fn length<T: AsRef<str> + ?Sized>(
74 min: Option<usize>,
75 max: Option<usize>,
76 message: impl Into<String>,
77 ) -> impl Fn(&T) -> Result<(), FieldError> {
78 let message = message.into();
79 move |value: &T| {
80 let value = value.as_ref();
81 let len = value.len();
82 if let Some(min) = min {
83 if len < min {
84 let mut params = HashMap::new();
85 params.insert("min".to_string(), serde_json::json!(min));
86 params.insert("max".to_string(), serde_json::json!(max));
87 params.insert("value".to_string(), serde_json::json!(len));
88 return Err(FieldError::with_params(
89 "length",
90 "length",
91 message.clone(),
92 params,
93 ));
94 }
95 }
96 if let Some(max) = max {
97 if len > max {
98 let mut params = HashMap::new();
99 params.insert("min".to_string(), serde_json::json!(min));
100 params.insert("max".to_string(), serde_json::json!(max));
101 params.insert("value".to_string(), serde_json::json!(len));
102 return Err(FieldError::with_params(
103 "length",
104 "length",
105 message.clone(),
106 params,
107 ));
108 }
109 }
110 Ok(())
111 }
112 }
113
114 pub fn range<T: PartialOrd + Copy + serde::Serialize>(
116 min: Option<T>,
117 max: Option<T>,
118 message: impl Into<String>,
119 ) -> impl Fn(&T) -> Result<(), FieldError> {
120 let message = message.into();
121 move |value: &T| {
122 if let Some(min) = min {
123 if *value < min {
124 let mut params = HashMap::new();
125 params.insert("min".to_string(), serde_json::json!(min));
126 params.insert("max".to_string(), serde_json::json!(max));
127 params.insert("value".to_string(), serde_json::json!(value));
128 return Err(FieldError::with_params(
129 "range",
130 "range",
131 message.clone(),
132 params,
133 ));
134 }
135 }
136 if let Some(max) = max {
137 if *value > max {
138 let mut params = HashMap::new();
139 params.insert("min".to_string(), serde_json::json!(min));
140 params.insert("max".to_string(), serde_json::json!(max));
141 params.insert("value".to_string(), serde_json::json!(value));
142 return Err(FieldError::with_params(
143 "range",
144 "range",
145 message.clone(),
146 params,
147 ));
148 }
149 }
150 Ok(())
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_custom_validator() {
161 let validator = Validator::<String>::new().rule(rules::length(
162 Some(3),
163 Some(10),
164 "Must be between 3 and 10 chars",
165 ));
166
167 assert!(validator.validate(&"ab".to_string()).is_err());
168 assert!(validator.validate(&"abc".to_string()).is_ok());
169 assert!(validator.validate(&"abcdefghijk".to_string()).is_err());
170 }
171
172 #[test]
173 fn test_range_validator() {
174 let validator = Validator::<i32>::new().rule(rules::range(
175 Some(18),
176 Some(100),
177 "Must be between 18 and 100",
178 ));
179
180 assert!(validator.validate(&17).is_err());
181 assert!(validator.validate(&18).is_ok());
182 assert!(validator.validate(&100).is_ok());
183 assert!(validator.validate(&101).is_err());
184 }
185}