kegani 0.1.0

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Custom validators for Kegani
//!
//! Provides validation utilities and error handling.

use serde::{Deserialize, Serialize};

/// Validation error
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
    pub field: String,
    pub message: String,
}

impl ValidationError {
    /// Create a new validation error
    pub fn new(field: &str, message: &str) -> Self {
        Self {
            field: field.to_string(),
            message: message.to_string(),
        }
    }
}

impl std::fmt::Display for ValidationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}: {}", self.field, self.message)
    }
}

/// Validation result
pub type ValidationResult<T> = std::result::Result<T, Vec<ValidationError>>;

/// Validator trait
pub trait Validator {
    /// Validate a value
    fn validate(&self, value: &str) -> bool;

    /// Get the error message
    fn message(&self) -> &str;
}

/// Email validator
pub struct EmailValidator;

impl Validator for EmailValidator {
    fn validate(&self, value: &str) -> bool {
        value.contains('@') && value.contains('.')
    }

    fn message(&self) -> &str {
        "Invalid email format"
    }
}

/// URL validator
pub struct UrlValidator;

impl Validator for UrlValidator {
    fn validate(&self, value: &str) -> bool {
        value.starts_with("http://") || value.starts_with("https://")
    }

    fn message(&self) -> &str {
        "Invalid URL format"
    }
}

/// Length validator
pub struct LengthValidator {
    min: usize,
    max: usize,
}

impl LengthValidator {
    /// Create a new length validator
    pub fn new(min: usize, max: usize) -> Self {
        Self { min, max }
    }

    /// Create with minimum only
    pub fn min(min: usize) -> Self {
        Self { min, max: usize::MAX }
    }

    /// Create with maximum only
    pub fn max(max: usize) -> Self {
        Self { min: 0, max }
    }
}

impl Validator for LengthValidator {
    fn validate(&self, value: &str) -> bool {
        let len = value.len();
        len >= self.min && len <= self.max
    }

    fn message(&self) -> &str {
        if self.max == usize::MAX {
            &format!("Length must be at least {}", self.min)
        } else if self.min == 0 {
            &format!("Length must be at most {}", self.max)
        } else {
            &format!("Length must be between {} and {}", self.min, self.max)
        }
    }
}

/// Regex validator
pub struct RegexValidator {
    pattern: regex::Regex,
}

impl RegexValidator {
    /// Create a new regex validator
    pub fn new(pattern: &str) -> Result<Self, regex::Error> {
        Ok(Self {
            pattern: regex::Regex::new(pattern)?,
        })
    }
}

impl Validator for RegexValidator {
    fn validate(&self, value: &str) -> bool {
        self.pattern.is_match(value)
    }

    fn message(&self) -> &str {
        "Invalid format"
    }
}

/// Range validator for numbers
pub struct RangeValidator {
    min: Option<f64>,
    max: Option<f64>,
}

impl RangeValidator {
    /// Create a new range validator
    pub fn new(min: Option<f64>, max: Option<f64>) -> Self {
        Self { min, max }
    }
}

impl Validator for RangeValidator {
    fn validate(&self, value: &str) -> bool {
        if let Ok(num) = value.parse::<f64>() {
            let min_ok = self.min.map_or(true, |m| num >= m);
            let max_ok = self.max.map_or(true, |m| num <= m);
            min_ok && max_ok
        } else {
            false
        }
    }

    fn message(&self) -> &str {
        "Value out of range"
    }
}

/// UUID validator
pub struct UuidValidator;

impl Validator for UuidValidator {
    fn validate(&self, value: &str) -> bool {
        uuid::Uuid::parse_str(value).is_ok()
    }

    fn message(&self) -> &str {
        "Invalid UUID format"
    }
}

/// Validator builder
pub struct ValidatorBuilder {
    validators: Vec<Box<dyn Validator>>,
}

impl ValidatorBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self {
            validators: Vec::new(),
        }
    }

    /// Add a validator
    pub fn add<V: Validator + 'static>(mut self, validator: V) -> Self {
        self.validators.push(Box::new(validator));
        self
    }

    /// Add email validation
    pub fn email(self) -> Self {
        self.add(EmailValidator)
    }

    /// Add URL validation
    pub fn url(self) -> Self {
        self.add(UrlValidator)
    }

    /// Add length validation
    pub fn length(self, min: usize, max: usize) -> Self {
        self.add(LengthValidator::new(min, max))
    }

    /// Add regex validation
    pub fn regex(self, pattern: &str) -> Result<Self, regex::Error> {
        Ok(self.add(RegexValidator::new(pattern)?))
    }

    /// Add range validation
    pub fn range(self, min: Option<f64>, max: Option<f64>) -> Self {
        self.add(RangeValidator::new(min, max))
    }

    /// Add UUID validation
    pub fn uuid(self) -> Self {
        self.add(UuidValidator)
    }

    /// Validate a value
    pub fn validate(&self, value: &str) -> ValidationResult<()> {
        let mut errors = Vec::new();

        for validator in &self.validators {
            if !validator.validate(value) {
                errors.push(ValidationError::new("field", validator.message()));
            }
        }

        if errors.is_empty() {
            Ok(())
        } else {
            Err(errors)
        }
    }
}

impl Default for ValidatorBuilder {
    fn default() -> Self {
        Self::new()
    }
}