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).map_err(ValidationError::from_validator_errors)
43    }
44
45    /// Validate and return the struct if valid, error otherwise.
46    fn validated(self) -> Result<Self, ValidationError>
47    where
48        Self: Sized,
49    {
50        Validate::validate(&self)?;
51        Ok(self)
52    }
53}
54
55// Blanket implementation for all types that implement validator::Validate
56impl<T: validator::Validate> Validate for T {}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use validator::Validate as ValidatorValidate;
62
63    #[derive(Debug, ValidatorValidate)]
64    struct TestUser {
65        #[validate(email)]
66        email: String,
67        #[validate(length(min = 3, max = 20))]
68        username: String,
69        #[validate(range(min = 18, max = 120))]
70        age: u8,
71    }
72
73    #[test]
74    fn valid_struct_passes() {
75        let user = TestUser {
76            email: "test@example.com".to_string(),
77            username: "testuser".to_string(),
78            age: 25,
79        };
80
81        assert!(Validate::validate(&user).is_ok());
82    }
83
84    #[test]
85    fn invalid_email_fails() {
86        let user = TestUser {
87            email: "not-an-email".to_string(),
88            username: "testuser".to_string(),
89            age: 25,
90        };
91
92        let result = Validate::validate(&user);
93        assert!(result.is_err());
94
95        let error = result.unwrap_err();
96        assert!(error.fields.iter().any(|f| f.field == "email"));
97    }
98
99    #[test]
100    fn invalid_length_fails() {
101        let user = TestUser {
102            email: "test@example.com".to_string(),
103            username: "ab".to_string(), // Too short
104            age: 25,
105        };
106
107        let result = Validate::validate(&user);
108        assert!(result.is_err());
109
110        let error = result.unwrap_err();
111        assert!(error
112            .fields
113            .iter()
114            .any(|f| f.field == "username" && f.code == "length"));
115    }
116
117    #[test]
118    fn invalid_range_fails() {
119        let user = TestUser {
120            email: "test@example.com".to_string(),
121            username: "testuser".to_string(),
122            age: 15, // Too young
123        };
124
125        let result = Validate::validate(&user);
126        assert!(result.is_err());
127
128        let error = result.unwrap_err();
129        assert!(error
130            .fields
131            .iter()
132            .any(|f| f.field == "age" && f.code == "range"));
133    }
134
135    #[test]
136    fn multiple_errors_collected() {
137        let user = TestUser {
138            email: "invalid".to_string(),
139            username: "ab".to_string(),
140            age: 150,
141        };
142
143        let result = Validate::validate(&user);
144        assert!(result.is_err());
145
146        let error = result.unwrap_err();
147        assert!(error.fields.len() >= 3);
148    }
149
150    #[test]
151    fn validated_returns_struct_on_success() {
152        let user = TestUser {
153            email: "test@example.com".to_string(),
154            username: "testuser".to_string(),
155            age: 25,
156        };
157
158        let result = user.validated();
159        assert!(result.is_ok());
160
161        let validated_user = result.unwrap();
162        assert_eq!(validated_user.email, "test@example.com");
163    }
164}