armature_validation/lib.rs
1//! Validation framework for Armature
2//!
3//! Provides comprehensive data validation with built-in validators,
4//! custom validation rules, and automatic request validation.
5//!
6//! # Examples
7//!
8//! ## Basic Validation
9//!
10//! ```
11//! use armature_validation::{Validate, ValidationError, NotEmpty, MinLength, IsEmail};
12//!
13//! struct UserInput {
14//! name: String,
15//! email: String,
16//! }
17//!
18//! impl Validate for UserInput {
19//! fn validate(&self) -> Result<(), Vec<ValidationError>> {
20//! let mut errors = Vec::new();
21//!
22//! if let Err(e) = NotEmpty::validate(&self.name, "name") {
23//! errors.push(e);
24//! }
25//! if let Err(e) = MinLength(3).validate(&self.name, "name") {
26//! errors.push(e);
27//! }
28//! if let Err(e) = IsEmail::validate(&self.email, "email") {
29//! errors.push(e);
30//! }
31//!
32//! if errors.is_empty() {
33//! Ok(())
34//! } else {
35//! Err(errors)
36//! }
37//! }
38//! }
39//!
40//! let input = UserInput {
41//! name: "John".to_string(),
42//! email: "john@example.com".to_string(),
43//! };
44//! assert!(input.validate().is_ok());
45//! ```
46//!
47//! ## Validation Rules Builder
48//!
49//! ```
50//! use armature_validation::{ValidationRules, NotEmpty, MinLength};
51//!
52//! // Create rules for a field
53//! let rules = ValidationRules::for_field("username")
54//! .add(|value, field| NotEmpty::validate(value, field))
55//! .add(|value, field| MinLength(3).validate(value, field));
56//!
57//! // Validate a value
58//! let result = rules.validate("john");
59//! assert!(result.is_ok());
60//! ```
61//!
62//! ## Number Validation
63//!
64//! ```
65//! use armature_validation::{Min, Max, InRange, IsPositive};
66//!
67//! // Min/Max validation
68//! assert!(Min(18).validate(25, "age").is_ok());
69//! assert!(Max(100).validate(50, "age").is_ok());
70//!
71//! // Range validation
72//! let range = InRange { min: 1, max: 10 };
73//! assert!(range.validate(5, "score").is_ok());
74//!
75//! // Positive number
76//! assert!(IsPositive::validate_i32(42, "count").is_ok());
77//! ```
78//!
79//! ## String Validators
80//!
81//! ```
82//! use armature_validation::{IsEmail, IsUrl, MaxLength, Matches};
83//!
84//! // Email validation
85//! assert!(IsEmail::validate("user@example.com", "email").is_ok());
86//! assert!(IsEmail::validate("invalid-email", "email").is_err());
87//!
88//! // URL validation
89//! assert!(IsUrl::validate("https://example.com", "website").is_ok());
90//! assert!(IsUrl::validate("not-a-url", "website").is_err());
91//!
92//! // Length constraints
93//! assert!(MaxLength(100).validate("short text", "description").is_ok());
94//! assert!(MaxLength(5).validate("this is too long", "description").is_err());
95//!
96//! // Pattern matching with regex
97//! let starts_with_capital = Matches::new("^[A-Z]").unwrap();
98//! assert!(starts_with_capital.validate("Hello", "name").is_ok());
99//! assert!(starts_with_capital.validate("hello", "name").is_err());
100//! ```
101//!
102//! ## Custom Validators
103//!
104//! ```
105//! use armature_validation::{Validator, ValidationError};
106//! use std::any::Any;
107//!
108//! // Create a custom validator
109//! struct IsStrongPassword;
110//!
111//! impl Validator for IsStrongPassword {
112//! fn validate(&self, value: &dyn Any, field: &str) -> Result<(), ValidationError> {
113//! let value = value.downcast_ref::<String>()
114//! .ok_or_else(|| ValidationError::new(field, "Expected string"))?;
115//!
116//! let has_uppercase = value.chars().any(|c| c.is_uppercase());
117//! let has_lowercase = value.chars().any(|c| c.is_lowercase());
118//! let has_digit = value.chars().any(|c| c.is_numeric());
119//! let long_enough = value.len() >= 8;
120//!
121//! if has_uppercase && has_lowercase && has_digit && long_enough {
122//! Ok(())
123//! } else {
124//! Err(ValidationError::new(
125//! field,
126//! "Password must be at least 8 characters with uppercase, lowercase, and digits"
127//! ))
128//! }
129//! }
130//!
131//! fn name(&self) -> &'static str {
132//! "IsStrongPassword"
133//! }
134//! }
135//!
136//! // Use the custom validator
137//! let validator = IsStrongPassword;
138//! let strong = "MyP@ssw0rd".to_string();
139//! let weak = "password".to_string();
140//!
141//! assert!(validator.validate(&strong, "password").is_ok());
142//! assert!(validator.validate(&weak, "password").is_err());
143//! assert_eq!(validator.name(), "IsStrongPassword");
144//! ```
145
146mod errors;
147mod pipe;
148mod rules;
149mod traits;
150mod validators;
151
152pub use errors::*;
153pub use pipe::*;
154pub use rules::*;
155pub use traits::*;
156pub use validators::*;
157
158/// Prelude for common imports.
159///
160/// ```
161/// use armature_validation::prelude::*;
162/// ```
163pub mod prelude {
164 pub use crate::errors::ValidationError;
165 pub use crate::pipe::ValidationPipe;
166 pub use crate::rules::ValidationRules;
167 pub use crate::traits::{Validate, Validator};
168 pub use crate::validators::{
169 InRange, IsAlpha, IsAlphanumeric, IsEmail, IsPositive, IsUrl, Matches, Max, MaxLength, Min,
170 MinLength, NotEmpty,
171 };
172}
173
174#[cfg(test)]
175mod tests {
176 #[test]
177 fn test_module_exports() {
178 // Ensure module compiles
179 }
180}