leptos_forms_rs/validation/
validators.rs1use crate::core::types::FieldValue;
8use regex::Regex;
9
10pub struct Validators;
12
13impl Validators {
14 pub fn required(value: &FieldValue) -> Result<(), String> {
16 match value {
17 FieldValue::String(s) if s.trim().is_empty() => {
18 Err("This field is required".to_string())
19 }
20 FieldValue::Null => Err("This field is required".to_string()),
21 FieldValue::Array(arr) if arr.is_empty() => Err("This field is required".to_string()),
22 _ => Ok(()),
23 }
24 }
25
26 pub fn email(value: &FieldValue) -> Result<(), String> {
28 if let FieldValue::String(email) = value {
29 let email_regex = Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap();
30 if email_regex.is_match(email) {
31 Ok(())
32 } else {
33 Err("Invalid email format".to_string())
34 }
35 } else {
36 Err("Email must be a string".to_string())
37 }
38 }
39
40 pub fn url(value: &FieldValue) -> Result<(), String> {
42 if let FieldValue::String(url) = value {
43 let url_regex = Regex::new(r"^https?://[^\s/$.?#].[^\s]*$").unwrap();
44 if url_regex.is_match(url) {
45 Ok(())
46 } else {
47 Err("Invalid URL format".to_string())
48 }
49 } else {
50 Err("URL must be a string".to_string())
51 }
52 }
53
54 pub fn min_length(value: &FieldValue, min: usize) -> Result<(), String> {
56 if let FieldValue::String(s) = value {
57 if s.len() >= min {
58 Ok(())
59 } else {
60 Err(format!("Minimum length is {} characters", min))
61 }
62 } else {
63 Err("Value must be a string".to_string())
64 }
65 }
66
67 pub fn max_length(value: &FieldValue, max: usize) -> Result<(), String> {
69 if let FieldValue::String(s) = value {
70 if s.len() <= max {
71 Ok(())
72 } else {
73 Err(format!("Maximum length is {} characters", max))
74 }
75 } else {
76 Err("Value must be a string".to_string())
77 }
78 }
79
80 pub fn min(value: &FieldValue, min: f64) -> Result<(), String> {
82 if let Some(num) = value.as_number() {
83 if num >= min {
84 Ok(())
85 } else {
86 Err(format!("Minimum value is {}", min))
87 }
88 } else {
89 Err("Value must be a number".to_string())
90 }
91 }
92
93 pub fn max(value: &FieldValue, max: f64) -> Result<(), String> {
95 if let Some(num) = value.as_number() {
96 if num <= max {
97 Ok(())
98 } else {
99 Err(format!("Maximum value is {}", max))
100 }
101 } else {
102 Err("Value must be a number".to_string())
103 }
104 }
105
106 pub fn pattern(value: &FieldValue, pattern: &str) -> Result<(), String> {
108 if let FieldValue::String(s) = value {
109 let regex = Regex::new(pattern).map_err(|_| "Invalid pattern".to_string())?;
110 if regex.is_match(s) {
111 Ok(())
112 } else {
113 Err("Value doesn't match required pattern".to_string())
114 }
115 } else {
116 Err("Value must be a string".to_string())
117 }
118 }
119
120 pub fn phone(value: &FieldValue) -> Result<(), String> {
122 if let FieldValue::String(phone) = value {
123 let phone_regex = Regex::new(r"^[\+]?[1-9][\d]{0,15}$").unwrap();
124 if phone_regex.is_match(phone) {
125 Ok(())
126 } else {
127 Err("Invalid phone number format".to_string())
128 }
129 } else {
130 Err("Phone number must be a string".to_string())
131 }
132 }
133
134 pub fn postal_code(value: &FieldValue) -> Result<(), String> {
136 if let FieldValue::String(code) = value {
137 let postal_regex = Regex::new(r"^\d{5}(-\d{4})?$").unwrap();
138 if postal_regex.is_match(code) {
139 Ok(())
140 } else {
141 Err("Invalid postal code format".to_string())
142 }
143 } else {
144 Err("Postal code must be a string".to_string())
145 }
146 }
147
148 pub fn credit_card(value: &FieldValue) -> Result<(), String> {
150 if let FieldValue::String(card) = value {
151 let digits: Vec<u32> = card
152 .chars()
153 .filter(|c| c.is_ascii_digit())
154 .map(|c| c.to_digit(10).unwrap())
155 .collect();
156
157 if digits.len() < 13 || digits.len() > 19 {
158 return Err("Invalid credit card number length".to_string());
159 }
160
161 let mut sum = 0;
163 let mut double = false;
164
165 for &digit in digits.iter().rev() {
166 if double {
167 let doubled = digit * 2;
168 sum += if doubled > 9 { doubled - 9 } else { doubled };
169 } else {
170 sum += digit;
171 }
172 double = !double;
173 }
174
175 if sum % 10 == 0 {
176 Ok(())
177 } else {
178 Err("Invalid credit card number".to_string())
179 }
180 } else {
181 Err("Credit card number must be a string".to_string())
182 }
183 }
184
185 pub fn date(value: &FieldValue) -> Result<(), String> {
187 if let FieldValue::Date(_) = value {
188 Ok(())
189 } else if let FieldValue::String(date_str) = value {
190 let date_regex = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
191 if date_regex.is_match(date_str) {
192 Ok(())
193 } else {
194 Err("Invalid date format (YYYY-MM-DD)".to_string())
195 }
196 } else {
197 Err("Value must be a date".to_string())
198 }
199 }
200
201 pub fn positive(value: &FieldValue) -> Result<(), String> {
203 if let Some(num) = value.as_number() {
204 if num > 0.0 {
205 Ok(())
206 } else {
207 Err("Value must be positive".to_string())
208 }
209 } else {
210 Err("Value must be a number".to_string())
211 }
212 }
213
214 pub fn negative(value: &FieldValue) -> Result<(), String> {
216 if let Some(num) = value.as_number() {
217 if num < 0.0 {
218 Ok(())
219 } else {
220 Err("Value must be negative".to_string())
221 }
222 } else {
223 Err("Value must be a number".to_string())
224 }
225 }
226
227 pub fn integer(value: &FieldValue) -> Result<(), String> {
229 if let Some(num) = value.as_number() {
230 if num.fract() == 0.0 {
231 Ok(())
232 } else {
233 Err("Value must be an integer".to_string())
234 }
235 } else {
236 Err("Value must be a number".to_string())
237 }
238 }
239
240 pub fn array_length(value: &FieldValue, min: usize, max: usize) -> Result<(), String> {
242 if let FieldValue::Array(arr) = value {
243 if arr.len() >= min && arr.len() <= max {
244 Ok(())
245 } else {
246 Err(format!("Array must have between {} and {} items", min, max))
247 }
248 } else {
249 Err("Value must be an array".to_string())
250 }
251 }
252
253 pub fn range(value: &FieldValue, min: f64, max: f64) -> Result<(), String> {
255 if let Some(num) = value.as_number() {
256 if num >= min && num <= max {
257 Ok(())
258 } else {
259 Err(format!("Value must be between {} and {}", min, max))
260 }
261 } else {
262 Err("Value must be a number".to_string())
263 }
264 }
265
266 pub fn not_empty(value: &FieldValue) -> Result<(), String> {
268 if value.is_empty() {
269 Err("Value cannot be empty".to_string())
270 } else {
271 Ok(())
272 }
273 }
274
275 pub fn empty(value: &FieldValue) -> Result<(), String> {
277 if value.is_empty() {
278 Ok(())
279 } else {
280 Err("Value must be empty".to_string())
281 }
282 }
283}