rustapi_validate/
validate.rs

1//! Validation trait and utilities.
2
3use crate::error::ValidationError;
4
5/// Trait for validatable types.
6///
7/// This trait wraps the `validator::Validate` trait and provides
8/// a RustAPI-native interface for validation.
9///
10/// ## Example
11///
12/// ```rust,ignore
13/// use rustapi_validate::prelude::*;
14/// use validator::Validate as ValidatorValidate;
15///
16/// #[derive(ValidatorValidate)]
17/// struct CreateUser {
18///     #[validate(email)]
19///     email: String,
20///     
21///     #[validate(length(min = 3, max = 50))]
22///     username: String,
23/// }
24///
25/// impl Validate for CreateUser {}
26///
27/// fn example() {
28///     let user = CreateUser {
29///         email: "invalid".to_string(),
30///         username: "ab".to_string(),
31///     };
32///
33///     match user.validate() {
34///         Ok(()) => println!("Valid!"),
35///         Err(e) => println!("Errors: {:?}", e.fields),
36///     }
37/// }
38/// ```
39pub trait Validate: validator::Validate {
40    /// Validate the struct and return a `ValidationError` on failure.
41    fn validate(&self) -> Result<(), ValidationError> {
42        validator::Validate::validate(self)
43            .map_err(ValidationError::from_validator_errors)
44    }
45
46    /// Validate and return the struct if valid, error otherwise.
47    fn validated(self) -> Result<Self, ValidationError>
48    where
49        Self: Sized,
50    {
51        Validate::validate(&self)?;
52        Ok(self)
53    }
54}
55
56// Blanket implementation for all types that implement validator::Validate
57impl<T: validator::Validate> Validate for T {}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use validator::Validate as ValidatorValidate;
63
64    #[derive(Debug, ValidatorValidate)]
65    struct TestUser {
66        #[validate(email)]
67        email: String,
68        #[validate(length(min = 3, max = 20))]
69        username: String,
70        #[validate(range(min = 18, max = 120))]
71        age: u8,
72    }
73
74    #[test]
75    fn valid_struct_passes() {
76        let user = TestUser {
77            email: "test@example.com".to_string(),
78            username: "testuser".to_string(),
79            age: 25,
80        };
81
82        assert!(Validate::validate(&user).is_ok());
83    }
84
85    #[test]
86    fn invalid_email_fails() {
87        let user = TestUser {
88            email: "not-an-email".to_string(),
89            username: "testuser".to_string(),
90            age: 25,
91        };
92
93        let result = Validate::validate(&user);
94        assert!(result.is_err());
95        
96        let error = result.unwrap_err();
97        assert!(error.fields.iter().any(|f| f.field == "email"));
98    }
99
100    #[test]
101    fn invalid_length_fails() {
102        let user = TestUser {
103            email: "test@example.com".to_string(),
104            username: "ab".to_string(), // Too short
105            age: 25,
106        };
107
108        let result = Validate::validate(&user);
109        assert!(result.is_err());
110        
111        let error = result.unwrap_err();
112        assert!(error.fields.iter().any(|f| f.field == "username" && f.code == "length"));
113    }
114
115    #[test]
116    fn invalid_range_fails() {
117        let user = TestUser {
118            email: "test@example.com".to_string(),
119            username: "testuser".to_string(),
120            age: 15, // Too young
121        };
122
123        let result = Validate::validate(&user);
124        assert!(result.is_err());
125        
126        let error = result.unwrap_err();
127        assert!(error.fields.iter().any(|f| f.field == "age" && f.code == "range"));
128    }
129
130    #[test]
131    fn multiple_errors_collected() {
132        let user = TestUser {
133            email: "invalid".to_string(),
134            username: "ab".to_string(),
135            age: 150,
136        };
137
138        let result = Validate::validate(&user);
139        assert!(result.is_err());
140        
141        let error = result.unwrap_err();
142        assert!(error.fields.len() >= 3);
143    }
144
145    #[test]
146    fn validated_returns_struct_on_success() {
147        let user = TestUser {
148            email: "test@example.com".to_string(),
149            username: "testuser".to_string(),
150            age: 25,
151        };
152
153        let result = user.validated();
154        assert!(result.is_ok());
155        
156        let validated_user = result.unwrap();
157        assert_eq!(validated_user.email, "test@example.com");
158    }
159}