ruskit/framework/validation/
mod.rs

1use std::collections::HashMap;
2use validator::{Validate, ValidationError};
3use serde::Deserialize;
4
5pub trait Rules {
6    fn rules() -> HashMap<&'static str, Vec<&'static str>>;
7}
8
9pub trait Validator {
10    fn validate(&self) -> Result<(), ValidationErrors>;
11}
12
13#[derive(Debug)]
14pub struct ValidationErrors {
15    errors: HashMap<String, Vec<String>>,
16}
17
18impl ValidationErrors {
19    pub fn new() -> Self {
20        Self {
21            errors: HashMap::new(),
22        }
23    }
24
25    pub fn add(&mut self, field: &str, message: &str) {
26        self.errors
27            .entry(field.to_string())
28            .or_insert_with(Vec::new)
29            .push(message.to_string());
30    }
31
32    pub fn errors(&self) -> &HashMap<String, Vec<String>> {
33        &self.errors
34    }
35
36    pub fn has_errors(&self) -> bool {
37        !self.errors.is_empty()
38    }
39}
40
41// Validation macros similar to Laravel
42#[macro_export]
43macro_rules! validate {
44    ($data:expr) => {
45        $data.validate()
46    };
47}
48
49// Example of a validation rule struct
50#[derive(Debug, Deserialize, Validate)]
51pub struct LoginRequest {
52    #[validate(email)]
53    pub email: String,
54    #[validate(length(min = 6))]
55    pub password: String,
56}
57
58// Example implementation of Rules trait
59impl Rules for LoginRequest {
60    fn rules() -> HashMap<&'static str, Vec<&'static str>> {
61        let mut rules = HashMap::new();
62        rules.insert("email", vec!["required", "email"]);
63        rules.insert("password", vec!["required", "min:6"]);
64        rules
65    }
66}
67
68// Built-in validation rules
69pub mod rules {
70    use super::*;
71    use regex::Regex;
72
73    pub fn required(value: &str) -> Result<(), ValidationError> {
74        if value.trim().is_empty() {
75            return Err(ValidationError::new("required"));
76        }
77        Ok(())
78    }
79
80    pub fn email(value: &str) -> Result<(), ValidationError> {
81        let re = Regex::new(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").unwrap();
82        if !re.is_match(value) {
83            return Err(ValidationError::new("email"));
84        }
85        Ok(())
86    }
87
88    pub fn min(value: &str, min: usize) -> Result<(), ValidationError> {
89        if value.len() < min {
90            return Err(ValidationError::new("min"));
91        }
92        Ok(())
93    }
94
95    pub fn max(value: &str, max: usize) -> Result<(), ValidationError> {
96        if value.len() > max {
97            return Err(ValidationError::new("max"));
98        }
99        Ok(())
100    }
101}