Skip to main content

pleme_error/
field_validator.rs

1//! Multi-field validation error collection and aggregation
2//!
3//! Provides helpers for collecting validation errors across multiple fields
4//! and converting them into ServiceError::ValidationErrors.
5//!
6//! # Example
7//! ```rust
8//! use pleme_error::FieldValidator;
9//!
10//! let mut validator = FieldValidator::new();
11//! validator.add_if(email.is_empty(), "email", "Email é obrigatório");
12//! validator.add_if(password.len() < 8, "password", "Senha muito curta");
13//!
14//! if !validator.is_empty() {
15//!     return Err(validator.into_service_error());
16//! }
17//! ```
18
19use crate::ServiceError;
20
21/// Helper for collecting validation errors across multiple fields
22#[derive(Debug, Default)]
23pub struct FieldValidator {
24    errors: Vec<(String, String)>,
25}
26
27impl FieldValidator {
28    /// Create a new field validator
29    pub fn new() -> Self {
30        Self { errors: Vec::new() }
31    }
32
33    /// Add an error for a specific field
34    pub fn add(&mut self, field: &str, message: &str) {
35        self.errors.push((field.to_string(), message.to_string()));
36    }
37
38    /// Add an error conditionally
39    pub fn add_if(&mut self, condition: bool, field: &str, message: &str) {
40        if condition {
41            self.add(field, message);
42        }
43    }
44
45    /// Check if there are any errors
46    pub fn is_empty(&self) -> bool {
47        self.errors.is_empty()
48    }
49
50    /// Get the number of errors
51    pub fn len(&self) -> usize {
52        self.errors.len()
53    }
54
55    /// Get errors as field tuples
56    pub fn errors(&self) -> &[(String, String)] {
57        &self.errors
58    }
59
60    /// Convert into ServiceError::ValidationErrors
61    pub fn into_service_error(self) -> ServiceError {
62        validation_errors_from_fields(self.errors)
63    }
64
65    /// Get errors as Vec of tuples (consumes self)
66    pub fn into_errors(self) -> Vec<(String, String)> {
67        self.errors
68    }
69
70    /// Get errors as static str tuples (for backward compatibility)
71    pub fn as_static_errors(&self) -> Vec<(&str, &str)> {
72        self.errors.iter()
73            .map(|(f, m)| (f.as_str(), m.as_str()))
74            .collect()
75    }
76}
77
78/// Create ServiceError::ValidationErrors from field tuples
79pub fn validation_errors_from_fields(errors: Vec<(String, String)>) -> ServiceError {
80    let error_map: std::collections::HashMap<String, Vec<String>> = errors.into_iter()
81        .fold(std::collections::HashMap::new(), |mut acc, (field, msg)| {
82            acc.entry(field).or_insert_with(Vec::new).push(msg);
83            acc
84        });
85
86    ServiceError::ValidationErrors(error_map)
87}
88
89/// Create ServiceError::ValidationErrors from static str tuples (helper)
90pub fn validation_from_fields(errors: Vec<(&str, &str)>) -> ServiceError {
91    let converted: Vec<(String, String)> = errors.into_iter()
92        .map(|(field, msg)| (field.to_string(), msg.to_string()))
93        .collect();
94    validation_errors_from_fields(converted)
95}