1use crate::request::form::Form;
2use regex::Regex;
3use chrono::{NaiveDate, NaiveDateTime};
4use std::net::IpAddr;
5use std::str::FromStr;
6use reqwest::Url;
7use uuid::Uuid;
8use ulid::Ulid;
9use serde_json::Value as JsonValue;
10
11fn pretty(value: String) -> String {
13 let temp: Vec<&str> = value.split('_').collect();
14 temp.join(" ")
15}
16
17fn get_value(form: &Form, field: &str) -> Option<String> {
18 form.values.get(field).cloned()
19}
20
21pub fn is_empty(form: &Form, field: &str) -> bool {
22 if let Some(val) = form.values.get(field) {
23 return val.is_empty();
24 }
25 if form.files.get(field).is_some() {
26 return false;
27 }
28 true
29}
30
31fn is_present(form: &Form, field: &str) -> bool {
32 form.values.contains_key(field) || form.files.contains_key(field)
33}
34
35pub async fn accepted<'r>(form: &'r Form, field: String, _args: Vec<String>) -> Option<String> {
37 if let Some(val) = get_value(form, &field) {
38 let accepted_vals = vec!["yes", "on", "1", "true"];
39 if accepted_vals.contains(&val.to_lowercase().as_str()) || val == "1" {
40 return None;
41 }
42 }
43 Some(format!("The {} must be accepted", pretty(field)))
44}
45
46pub async fn accepted_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
47 if args.len() < 2 { return None; }
48 let another_field = &args[0];
49 let expected_val = &args[1];
50
51 if let Some(val) = get_value(form, another_field) {
52 if val == *expected_val {
53 return accepted(form, field, Vec::new()).await;
54 }
55 }
56 None
57}
58
59pub async fn boolean(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
60 if let Some(val) = get_value(form, &field) {
61 let bool_vals = vec!["true", "false", "1", "0", "on", "off", "yes", "no"];
62 if bool_vals.contains(&val.to_lowercase().as_str()) {
63 return None;
64 }
65 }
66 Some(format!("The {} must be a boolean", pretty(field)))
67}
68
69pub async fn declined(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
70 if let Some(val) = get_value(form, &field) {
71 let declined_vals = vec!["no", "off", "0", "false"];
72 if declined_vals.contains(&val.to_lowercase().as_str()) {
73 return None;
74 }
75 }
76 Some(format!("The {} must be declined", pretty(field)))
77}
78
79pub async fn declined_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
80 if args.len() < 2 { return None; }
81 let another_field = &args[0];
82 let expected_val = &args[1];
83
84 if let Some(val) = get_value(form, another_field) {
85 if val == *expected_val {
86 return declined(form, field, Vec::new()).await;
87 }
88 }
89 None
90}
91
92pub async fn active_url(_form: &Form, field: String, _args: Vec<String>) -> Option<String> {
94 if let Some(val) = get_value(_form, &field) {
95 if let Ok(url) = Url::parse(&val) {
96 if let Some(host) = url.host_str() {
97 if tokio::net::lookup_host(format!("{}:80", host)).await.is_ok() {
98 return None;
99 }
100 }
101 }
102 }
103 Some(format!("The {} is not a valid active URL", pretty(field)))
104}
105
106pub async fn alpha(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
107 if let Some(val) = get_value(form, &field) {
108 if !val.is_empty() && val.chars().all(|c| c.is_alphabetic()) {
109 return None;
110 }
111 }
112 Some(format!("The {} must only contain alphabetic characters", pretty(field)))
113}
114
115pub async fn alpha_dash(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
116 if let Some(val) = get_value(form, &field) {
117 if !val.is_empty() && val.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
118 return None;
119 }
120 }
121 Some(format!("The {} must only contain letters, numbers, dashes and underscores", pretty(field)))
122}
123
124pub async fn alpha_numeric(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
125 if let Some(val) = get_value(form, &field) {
126 if !val.is_empty() && val.chars().all(|c| c.is_alphanumeric()) {
127 return None;
128 }
129 }
130 Some(format!("The {} must only contain alphanumeric characters", pretty(field)))
131}
132
133pub async fn ascii(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
134 if let Some(val) = get_value(form, &field) {
135 if val.is_ascii() {
136 return None;
137 }
138 }
139 Some(format!("The {} must only contain ASCII characters", pretty(field)))
140}
141
142pub async fn confirmed(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
143 let confirmation_field = format!("{}_confirmation", field);
144 if let Some(val) = get_value(form, &field) {
145 if let Some(conf) = get_value(form, &confirmation_field) {
146 if val == conf {
147 return None;
148 }
149 }
150 }
151 Some(format!("The {} confirmation does not match", pretty(field)))
152}
153
154pub async fn different(form: &Form, field: String, args: Vec<String>) -> Option<String> {
155 if args.is_empty() { return None; }
156 let another_field = &args[0];
157 if let Some(val) = get_value(form, &field) {
158 if let Some(another_val) = get_value(form, another_field) {
159 if val != another_val {
160 return None;
161 }
162 } else {
163 return None;
164 }
165 }
166 Some(format!("The {} must be different from {}", pretty(field), pretty(another_field.clone())))
167}
168
169pub async fn doesnt_start_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
170 if let Some(val) = get_value(form, &field) {
171 for arg in &args {
172 if val.starts_with(arg) {
173 return Some(format!("The {} must not start with {}", pretty(field), arg));
174 }
175 }
176 }
177 None
178}
179
180pub async fn doesnt_end_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
181 if let Some(val) = get_value(form, &field) {
182 for arg in &args {
183 if val.ends_with(arg) {
184 return Some(format!("The {} must not end with {}", pretty(field), arg));
185 }
186 }
187 }
188 None
189}
190
191pub async fn email(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
192 if let Some(val) = get_value(form, &field) {
193 let email_regex = Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap();
194 if email_regex.is_match(&val) { return None; }
195 }
196 Some(format!("The {} must be a valid email address", pretty(field)))
197}
198
199pub async fn ends_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
200 if let Some(val) = get_value(form, &field) {
201 for arg in &args {
202 if val.ends_with(arg) {
203 return None;
204 }
205 }
206 }
207 Some(format!("The {} must end with one of: {}", pretty(field), args.join(", ")))
208}
209
210pub async fn hex_color(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
211 if let Some(val) = get_value(form, &field) {
212 let hex_regex = Regex::new(r"^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$").unwrap();
213 if hex_regex.is_match(&val) { return None; }
214 }
215 Some(format!("The {} must be a valid hexadecimal color", pretty(field)))
216}
217
218pub async fn in_rule(form: &Form, field: String, args: Vec<String>) -> Option<String> {
219 if let Some(val) = get_value(form, &field) {
220 if args.contains(&val) {
221 return None;
222 }
223 }
224 Some(format!("The selected {} is invalid", pretty(field)))
225}
226
227pub async fn ip(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
228 if let Some(val) = get_value(form, &field) {
229 if IpAddr::from_str(&val).is_ok() { return None; }
230 }
231 Some(format!("The {} must be a valid IP address", pretty(field)))
232}
233
234pub async fn ipv4(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
235 if let Some(val) = get_value(form, &field) {
236 if let Ok(IpAddr::V4(_)) = IpAddr::from_str(&val) { return None; }
237 }
238 Some(format!("The {} must be a valid IPv4 address", pretty(field)))
239}
240
241pub async fn ipv6(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
242 if let Some(val) = get_value(form, &field) {
243 if let Ok(IpAddr::V6(_)) = IpAddr::from_str(&val) { return None; }
244 }
245 Some(format!("The {} must be a valid IPv6 address", pretty(field)))
246}
247
248pub async fn json(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
249 if let Some(val) = get_value(form, &field) {
250 if serde_json::from_str::<JsonValue>(&val).is_ok() { return None; }
251 }
252 Some(format!("The {} must be a valid JSON string", pretty(field)))
253}
254
255pub async fn lowercase(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
256 if let Some(val) = get_value(form, &field) {
257 if val == val.to_lowercase() { return None; }
258 }
259 Some(format!("The {} must be lowercase", pretty(field)))
260}
261
262pub async fn mac_address(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
263 if let Some(val) = get_value(form, &field) {
264 let mac_regex = Regex::new(r"^([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2})$").unwrap();
265 if mac_regex.is_match(&val) { return None; }
266 }
267 Some(format!("The {} must be a valid MAC address", pretty(field)))
268}
269
270pub async fn not_in(form: &Form, field: String, args: Vec<String>) -> Option<String> {
271 if let Some(val) = get_value(form, &field) {
272 if !args.contains(&val) {
273 return None;
274 }
275 }
276 Some(format!("The selected {} is invalid", pretty(field)))
277}
278
279pub async fn regex(form: &Form, field: String, args: Vec<String>) -> Option<String> {
280 if args.is_empty() { return None; }
281 if let Some(val) = get_value(form, &field) {
282 if let Ok(re) = Regex::new(&args[0]) {
283 if re.is_match(&val) { return None; }
284 }
285 }
286 Some(format!("The {} format is invalid", pretty(field)))
287}
288
289pub async fn not_regex(form: &Form, field: String, args: Vec<String>) -> Option<String> {
290 if args.is_empty() { return None; }
291 if let Some(val) = get_value(form, &field) {
292 if let Ok(re) = Regex::new(&args[0]) {
293 if !re.is_match(&val) { return None; }
294 }
295 }
296 Some(format!("The {} format is invalid", pretty(field)))
297}
298
299pub async fn same(form: &Form, field: String, args: Vec<String>) -> Option<String> {
300 if args.is_empty() { return None; }
301 let another_field = &args[0];
302 if let Some(val) = get_value(form, &field) {
303 if let Some(another_val) = get_value(form, another_field) {
304 if val == another_val { return None; }
305 }
306 }
307 Some(format!("The {} and {} must match", pretty(field), pretty(another_field.clone())))
308}
309
310pub async fn starts_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
311 if let Some(val) = get_value(form, &field) {
312 for arg in &args {
313 if val.starts_with(arg) {
314 return None;
315 }
316 }
317 }
318 Some(format!("The {} must start with one of: {}", pretty(field), args.join(", ")))
319}
320
321pub async fn uppercase(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
322 if let Some(val) = get_value(form, &field) {
323 if val == val.to_uppercase() { return None; }
324 }
325 Some(format!("The {} must be uppercase", pretty(field)))
326}
327
328pub async fn url(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
329 if let Some(val) = get_value(form, &field) {
330 if Url::parse(&val).is_ok() { return None; }
331 }
332 Some(format!("The {} must be a valid URL", pretty(field)))
333}
334
335pub async fn uuid(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
336 if let Some(val) = get_value(form, &field) {
337 if Uuid::parse_str(&val).is_ok() { return None; }
338 }
339 Some(format!("The {} must be a valid UUID", pretty(field)))
340}
341
342pub async fn ulid(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
343 if let Some(val) = get_value(form, &field) {
344 if Ulid::from_str(&val).is_ok() { return None; }
345 }
346 Some(format!("The {} must be a valid ULID", pretty(field)))
347}
348
349pub async fn between(form: &Form, field: String, args: Vec<String>) -> Option<String> {
351 if args.len() < 2 { return None; }
352 let min: f64 = args[0].parse().unwrap_or(0.0);
353 let max: f64 = args[1].parse().unwrap_or(0.0);
354
355 if let Some(val) = get_value(form, &field) {
356 if let Ok(num) = val.parse::<f64>() {
357 if num >= min && num <= max { return None; }
358 return Some(format!("The {} must be between {} and {}", pretty(field), min, max));
359 }
360 if val.len() >= min as usize && val.len() <= max as usize { return None; }
361 return Some(format!("The {} must be between {} and {} characters", pretty(field), min, max));
362 }
363 if let Some(file) = form.files.get(&field) {
364 let size_kb = file.content.len() / 1024;
365 if size_kb >= min as usize && size_kb <= max as usize { return None; }
366 return Some(format!("The {} must be between {} and {} kilobytes", pretty(field), min, max));
367 }
368 None
369}
370
371pub async fn decimal(form: &Form, field: String, args: Vec<String>) -> Option<String> {
372 if let Some(val) = get_value(form, &field) {
373 if let Ok(_) = val.parse::<f64>() {
374 if val.contains('.') {
375 let parts: Vec<&str> = val.split('.').collect();
376 if parts.len() == 2 {
377 let decimal_places = parts[1].len();
378 if args.len() >= 1 {
379 let min: usize = args[0].parse().unwrap_or(0);
380 if args.len() >= 2 {
381 let max: usize = args[1].parse().unwrap_or(min);
382 if decimal_places >= min && decimal_places <= max { return None; }
383 return Some(format!("The {} must have between {} and {} decimal places", pretty(field), min, max));
384 }
385 if decimal_places == min { return None; }
386 return Some(format!("The {} must have {} decimal places", pretty(field), min));
387 }
388 return None;
389 }
390 }
391 }
392 }
393 Some(format!("The {} must be a decimal", pretty(field)))
394}
395
396pub async fn digits(form: &Form, field: String, args: Vec<String>) -> Option<String> {
397 if args.is_empty() { return None; }
398 let len: usize = args[0].parse().unwrap_or(0);
399 if let Some(val) = get_value(form, &field) {
400 if val.chars().all(|c| c.is_numeric()) && val.len() == len {
401 return None;
402 }
403 }
404 Some(format!("The {} must be {} digits", pretty(field), len))
405}
406
407pub async fn digits_between(form: &Form, field: String, args: Vec<String>) -> Option<String> {
408 if args.len() < 2 { return None; }
409 let min: usize = args[0].parse().unwrap_or(0);
410 let max: usize = args[1].parse().unwrap_or(0);
411 if let Some(val) = get_value(form, &field) {
412 if val.chars().all(|c| c.is_numeric()) && val.len() >= min && val.len() <= max {
413 return None;
414 }
415 }
416 Some(format!("The {} must be between {} and {} digits", pretty(field), min, max))
417}
418
419pub async fn gt(form: &Form, field: String, args: Vec<String>) -> Option<String> {
420 if args.is_empty() { return None; }
421 let another_field = &args[0];
422 if let Some(val) = get_value(form, &field) {
423 if let Ok(v1) = val.parse::<f64>() {
424 if let Some(another_val) = get_value(form, another_field) {
425 if let Ok(v2) = another_val.parse::<f64>() {
426 if v1 > v2 { return None; }
427 }
428 } else if let Ok(v2) = another_field.parse::<f64>() {
429 if v1 > v2 { return None; }
430 }
431 }
432 }
433 Some(format!("The {} must be greater than {}", pretty(field), pretty(another_field.clone())))
434}
435
436pub async fn gte(form: &Form, field: String, args: Vec<String>) -> Option<String> {
437 if args.is_empty() { return None; }
438 let another_field = &args[0];
439 if let Some(val) = get_value(form, &field) {
440 if let Ok(v1) = val.parse::<f64>() {
441 if let Some(another_val) = get_value(form, another_field) {
442 if let Ok(v2) = another_val.parse::<f64>() {
443 if v1 >= v2 { return None; }
444 }
445 } else if let Ok(v2) = another_field.parse::<f64>() {
446 if v1 >= v2 { return None; }
447 }
448 }
449 }
450 Some(format!("The {} must be greater than or equal {}", pretty(field), pretty(another_field.clone())))
451}
452
453pub async fn integer(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
454 if let Some(val) = get_value(form, &field) {
455 if val.parse::<i128>().is_ok() { return None; }
456 }
457 Some(format!("The {} must be an integer", pretty(field)))
458}
459
460pub async fn lt(form: &Form, field: String, args: Vec<String>) -> Option<String> {
461 if args.is_empty() { return None; }
462 let another_field = &args[0];
463 if let Some(val) = get_value(form, &field) {
464 if let Ok(v1) = val.parse::<f64>() {
465 if let Some(another_val) = get_value(form, another_field) {
466 if let Ok(v2) = another_val.parse::<f64>() {
467 if v1 < v2 { return None; }
468 }
469 } else if let Ok(v2) = another_field.parse::<f64>() {
470 if v1 < v2 { return None; }
471 }
472 }
473 }
474 Some(format!("The {} must be less than {}", pretty(field), pretty(another_field.clone())))
475}
476
477pub async fn lte(form: &Form, field: String, args: Vec<String>) -> Option<String> {
478 if args.is_empty() { return None; }
479 let another_field = &args[0];
480 if let Some(val) = get_value(form, &field) {
481 if let Ok(v1) = val.parse::<f64>() {
482 if let Some(another_val) = get_value(form, another_field) {
483 if let Ok(v2) = another_val.parse::<f64>() {
484 if v1 <= v2 { return None; }
485 }
486 } else if let Ok(v2) = another_field.parse::<f64>() {
487 if v1 <= v2 { return None; }
488 }
489 }
490 }
491 Some(format!("The {} must be less than or equal {}", pretty(field), pretty(another_field.clone())))
492}
493
494pub async fn max(form: &Form, field: String, args: Vec<String>) -> Option<String> {
495 if args.is_empty() { return None; }
496 let max_val: f64 = args[0].parse().unwrap_or(0.0);
497
498 if let Some(val) = get_value(form, &field) {
499 if let Ok(num) = val.parse::<f64>() {
500 if num <= max_val { return None; }
501 return Some(format!("The {} must not be greater than {}", pretty(field), max_val));
502 }
503 if val.len() <= max_val as usize { return None; }
504 return Some(format!("The {} must not be greater than {} characters", pretty(field), max_val));
505 }
506 if let Some(file) = form.files.get(&field) {
507 let size_kb = file.content.len() / 1024;
508 if size_kb <= max_val as usize { return None; }
509 return Some(format!("The {} must not be greater than {} kilobytes", pretty(field), max_val));
510 }
511 None
512}
513
514pub async fn min(form: &Form, field: String, args: Vec<String>) -> Option<String> {
515 if args.is_empty() { return None; }
516 let min_val: f64 = args[0].parse().unwrap_or(0.0);
517
518 if let Some(val) = get_value(form, &field) {
519 if let Ok(num) = val.parse::<f64>() {
520 if num >= min_val { return None; }
521 return Some(format!("The {} must be at least {}", pretty(field), min_val));
522 }
523 if val.len() >= min_val as usize { return None; }
524 return Some(format!("The {} must be at least {} characters", pretty(field), min_val));
525 }
526 if let Some(file) = form.files.get(&field) {
527 let size_kb = file.content.len() / 1024;
528 if size_kb >= min_val as usize { return None; }
529 return Some(format!("The {} must be at least {} kilobytes", pretty(field), min_val));
530 }
531 None
532}
533
534pub async fn max_digits(form: &Form, field: String, args: Vec<String>) -> Option<String> {
535 if args.is_empty() { return None; }
536 let max: usize = args[0].parse().unwrap_or(0);
537 if let Some(val) = get_value(form, &field) {
538 if val.chars().all(|c| c.is_numeric()) && val.len() <= max {
539 return None;
540 }
541 }
542 Some(format!("The {} must not have more than {} digits", pretty(field), max))
543}
544
545pub async fn min_digits(form: &Form, field: String, args: Vec<String>) -> Option<String> {
546 if args.is_empty() { return None; }
547 let min: usize = args[0].parse().unwrap_or(0);
548 if let Some(val) = get_value(form, &field) {
549 if val.chars().all(|c| c.is_numeric()) && val.len() >= min {
550 return None;
551 }
552 }
553 Some(format!("The {} must have at least {} digits", pretty(field), min))
554}
555
556pub async fn multiple_of(form: &Form, field: String, args: Vec<String>) -> Option<String> {
557 if args.is_empty() { return None; }
558 let factor: f64 = args[0].parse().unwrap_or(1.0);
559 if let Some(val) = get_value(form, &field) {
560 if let Ok(num) = val.parse::<f64>() {
561 if num % factor == 0.0 { return None; }
562 }
563 }
564 Some(format!("The {} must be a multiple of {}", pretty(field), factor))
565}
566
567pub async fn numeric(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
568 if let Some(val) = get_value(form, &field) {
569 if val.parse::<f64>().is_ok() { return None; }
570 }
571 Some(format!("The {} must be a number", pretty(field)))
572}
573
574fn parse_date(date_str: &str) -> Option<NaiveDateTime> {
576 let formats = vec![
577 "%Y-%m-%d %H:%M:%S",
578 "%Y-%m-%d",
579 "%d-%m-%Y",
580 "%m/%d/%Y",
581 ];
582 for format in formats {
583 if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format) {
584 return Some(dt);
585 }
586 if let Ok(d) = NaiveDate::parse_from_str(date_str, format) {
587 return Some(d.and_hms_opt(0, 0, 0).unwrap());
588 }
589 }
590 None
591}
592
593pub async fn after(form: &Form, field: String, args: Vec<String>) -> Option<String> {
594 if args.is_empty() { return None; }
595 let target_date_str = if let Some(val) = get_value(form, &args[0]) { val } else { args[0].clone() };
596 if let Some(val) = get_value(form, &field) {
597 if let (Some(d1), Some(d2)) = (parse_date(&val), parse_date(&target_date_str)) {
598 if d1 > d2 { return None; }
599 }
600 }
601 Some(format!("The {} must be a date after {}", pretty(field), target_date_str))
602}
603
604pub async fn after_or_equal(form: &Form, field: String, args: Vec<String>) -> Option<String> {
605 if args.is_empty() { return None; }
606 let target_date_str = if let Some(val) = get_value(form, &args[0]) { val } else { args[0].clone() };
607 if let Some(val) = get_value(form, &field) {
608 if let (Some(d1), Some(d2)) = (parse_date(&val), parse_date(&target_date_str)) {
609 if d1 >= d2 { return None; }
610 }
611 }
612 Some(format!("The {} must be a date after or equal to {}", pretty(field), target_date_str))
613}
614
615pub async fn before(form: &Form, field: String, args: Vec<String>) -> Option<String> {
616 if args.is_empty() { return None; }
617 let target_date_str = if let Some(val) = get_value(form, &args[0]) { val } else { args[0].clone() };
618 if let Some(val) = get_value(form, &field) {
619 if let (Some(d1), Some(d2)) = (parse_date(&val), parse_date(&target_date_str)) {
620 if d1 < d2 { return None; }
621 }
622 }
623 Some(format!("The {} must be a date before {}", pretty(field), target_date_str))
624}
625
626pub async fn before_or_equal(form: &Form, field: String, args: Vec<String>) -> Option<String> {
627 if args.is_empty() { return None; }
628 let target_date_str = if let Some(val) = get_value(form, &args[0]) { val } else { args[0].clone() };
629 if let Some(val) = get_value(form, &field) {
630 if let (Some(d1), Some(d2)) = (parse_date(&val), parse_date(&target_date_str)) {
631 if d1 <= d2 { return None; }
632 }
633 }
634 Some(format!("The {} must be a date before or equal to {}", pretty(field), target_date_str))
635}
636
637pub async fn date(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
638 if let Some(val) = get_value(form, &field) {
639 if parse_date(&val).is_some() { return None; }
640 }
641 Some(format!("The {} is not a valid date", pretty(field)))
642}
643
644pub async fn date_equals(form: &Form, field: String, args: Vec<String>) -> Option<String> {
645 if args.is_empty() { return None; }
646 let target_date_str = if let Some(val) = get_value(form, &args[0]) { val } else { args[0].clone() };
647 if let Some(val) = get_value(form, &field) {
648 if let (Some(d1), Some(d2)) = (parse_date(&val), parse_date(&target_date_str)) {
649 if d1 == d2 { return None; }
650 }
651 }
652 Some(format!("The {} must be a date equal to {}", pretty(field), target_date_str))
653}
654
655pub async fn date_format(form: &Form, field: String, args: Vec<String>) -> Option<String> {
656 if args.is_empty() { return None; }
657 let format = &args[0];
658 if let Some(val) = get_value(form, &field) {
659 if NaiveDateTime::parse_from_str(&val, format).is_ok() || NaiveDate::parse_from_str(&val, format).is_ok() {
660 return None;
661 }
662 }
663 Some(format!("The {} does not match the format {}", pretty(field), format))
664}
665
666pub async fn file(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
668 if form.files.get(&field).is_some() { return None; }
669 Some(format!("The {} must be a file", pretty(field)))
670}
671
672pub async fn image(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
673 if let Some(file) = form.files.get(&field) {
674 let image_mimes = vec!["image/jpeg", "image/png", "image/gif", "image/bmp", "image/svg+xml", "image/webp"];
675 if image_mimes.contains(&file.mime.as_str()) {
676 return None;
677 }
678 }
679 Some(format!("The {} must be an image", pretty(field)))
680}
681
682pub async fn mimetypes(form: &Form, field: String, args: Vec<String>) -> Option<String> {
683 if let Some(file) = form.files.get(&field) {
684 if args.contains(&file.mime) {
685 return None;
686 }
687 }
688 Some(format!("The {} must be a file of type: {}", pretty(field), args.join(", ")))
689}
690
691pub async fn mimes(form: &Form, field: String, args: Vec<String>) -> Option<String> {
692 if let Some(file) = form.files.get(&field) {
693 let parts: Vec<&str> = file.name.split('.').collect();
694 if let Some(ext) = parts.last() {
695 if args.contains(&ext.to_string()) {
696 return None;
697 }
698 }
699 }
700 Some(format!("The {} must be a file of type: {}", pretty(field), args.join(", ")))
701}
702
703pub async fn extensions(form: &Form, field: String, args: Vec<String>) -> Option<String> {
704 mimes(form, field, args).await
705}
706
707pub async fn required(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
709 if !is_empty(form, &field) { return None; }
710 Some(format!("The {} field is required", pretty(field)))
711}
712
713pub async fn required_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
714 if args.len() < 2 { return None; }
715 let another_field = &args[0];
716 let expected_val = &args[1];
717
718 if let Some(val) = get_value(form, another_field) {
719 if val == *expected_val {
720 return required(form, field, Vec::new()).await;
721 }
722 }
723 None
724}
725
726pub async fn required_if_accepted(form: &Form, field: String, args: Vec<String>) -> Option<String> {
727 if args.is_empty() { return None; }
728 let another_field = &args[0];
729 if accepted(form, another_field.clone(), Vec::new()).await.is_none() {
730 return required(form, field, Vec::new()).await;
731 }
732 None
733}
734
735pub async fn required_unless(form: &Form, field: String, args: Vec<String>) -> Option<String> {
736 if args.len() < 2 { return None; }
737 let another_field = &args[0];
738 let expected_val = &args[1];
739
740 if let Some(val) = get_value(form, another_field) {
741 if val != *expected_val {
742 return required(form, field, Vec::new()).await;
743 }
744 } else {
745 return required(form, field, Vec::new()).await;
746 }
747 None
748}
749
750pub async fn required_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
751 for arg in &args {
752 if !is_empty(form, arg) {
753 return required(form, field, Vec::new()).await;
754 }
755 }
756 None
757}
758
759pub async fn required_without(form: &Form, field: String, args: Vec<String>) -> Option<String> {
760 if let Some(err) = required(form, field.clone(), args.clone()).await {
761 for arg in args {
762 if let Some(_) = required(form, String::from(arg.to_string()), Default::default()).await {
763 return Some(err);
764 }
765 }
766 }
767 Some(String::new())
768}
769
770pub async fn required_with_all(form: &Form, field: String, args: Vec<String>) -> Option<String> {
771 for arg in &args {
772 if is_empty(form, arg) {
773 return None;
774 }
775 }
776 required(form, field, Vec::new()).await
777}
778
779pub async fn required_without_all(form: &Form, field: String, args: Vec<String>) -> Option<String> {
780 for arg in &args {
781 if !is_empty(form, arg) {
782 return None;
783 }
784 }
785 required(form, field, Vec::new()).await
786}
787
788pub async fn prohibited(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
789 if is_empty(form, &field) { return None; }
790 Some(format!("The {} field is prohibited", pretty(field)))
791}
792
793pub async fn prohibited_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
794 if args.len() < 2 { return None; }
795 let another_field = &args[0];
796 let expected_val = &args[1];
797
798 if let Some(val) = get_value(form, another_field) {
799 if val == *expected_val {
800 return prohibited(form, field, Vec::new()).await;
801 }
802 }
803 None
804}
805
806pub async fn prohibited_unless(form: &Form, field: String, args: Vec<String>) -> Option<String> {
807 if args.len() < 2 { return None; }
808 let another_field = &args[0];
809 let expected_val = &args[1];
810
811 if let Some(val) = get_value(form, another_field) {
812 if val != *expected_val {
813 return prohibited(form, field, Vec::new()).await;
814 }
815 } else {
816 return prohibited(form, field, Vec::new()).await;
817 }
818 None
819}
820
821pub async fn prohibited_with(form: &Form, field: String, args: Vec<String>) -> Option<String> {
822 for arg in &args {
823 if !is_empty(form, arg) {
824 return prohibited(form, field, Vec::new()).await;
825 }
826 }
827 None
828}
829
830pub async fn prohibited_with_all(form: &Form, field: String, args: Vec<String>) -> Option<String> {
831 for arg in &args {
832 if is_empty(form, arg) {
833 return None;
834 }
835 }
836 prohibited(form, field, Vec::new()).await
837}
838
839pub async fn filled(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
840 if is_present(form, &field) && is_empty(form, &field) {
841 return Some(format!("The {} field must have a value", pretty(field)));
842 }
843 None
844}
845
846pub async fn missing(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
847 if is_present(form, &field) {
848 return Some(format!("The {} field must be missing", pretty(field)));
849 }
850 None
851}
852
853pub async fn missing_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
854 if args.len() < 2 { return None; }
855 let another_field = &args[0];
856 let expected_val = &args[1];
857 if let Some(val) = get_value(form, another_field) {
858 if val == *expected_val {
859 return missing(form, field, Vec::new()).await;
860 }
861 }
862 None
863}
864
865pub async fn missing_unless(form: &Form, field: String, args: Vec<String>) -> Option<String> {
866 if args.len() < 2 { return None; }
867 let another_field = &args[0];
868 let expected_val = &args[1];
869 if let Some(val) = get_value(form, another_field) {
870 if val != *expected_val {
871 return missing(form, field, Vec::new()).await;
872 }
873 } else {
874 return missing(form, field, Vec::new()).await;
875 }
876 None
877}
878
879pub async fn present(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
880 if !is_present(form, &field) {
881 return Some(format!("The {} field must be present", pretty(field)));
882 }
883 None
884}
885
886pub async fn present_if(form: &Form, field: String, args: Vec<String>) -> Option<String> {
887 if args.len() < 2 { return None; }
888 let another_field = &args[0];
889 let expected_val = &args[1];
890 if let Some(val) = get_value(form, another_field) {
891 if val == *expected_val {
892 return present(form, field, Vec::new()).await;
893 }
894 }
895 None
896}
897
898pub async fn present_unless(form: &Form, field: String, args: Vec<String>) -> Option<String> {
899 if args.len() < 2 { return None; }
900 let another_field = &args[0];
901 let expected_val = &args[1];
902 if let Some(val) = get_value(form, another_field) {
903 if val != *expected_val {
904 return present(form, field, Vec::new()).await;
905 }
906 } else {
907 return present(form, field, Vec::new()).await;
908 }
909 None
910}
911
912pub async fn string(form: &Form, field: String, _args: Vec<String>) -> Option<String> {
913 if get_value(form, &field).is_some() { return None; }
914 Some(format!("The {} must be a string", pretty(field)))
915}
916
917pub async fn size(form: &Form, field: String, args: Vec<String>) -> Option<String> {
918 if args.is_empty() { return None; }
919 let size: usize = args[0].parse().unwrap_or(0);
920
921 if let Some(val) = get_value(form, &field) {
922 if let Ok(num) = val.parse::<f64>() {
923 if num == size as f64 { return None; }
924 return Some(format!("The {} must be {}", pretty(field), size));
925 }
926 if val.len() == size { return None; }
927 return Some(format!("The {} must be {} characters", pretty(field), size));
928 }
929 if let Some(file) = form.files.get(&field) {
930 let size_kb = file.content.len() / 1024;
931 if size_kb == size { return None; }
932 return Some(format!("The {} must be {} kilobytes", pretty(field), size));
933 }
934 None
935}
936