presentar_core/
validation.rs

1//! Declarative validation system for forms and inputs.
2//!
3//! This module provides a flexible validation framework with:
4//! - Built-in validators (required, min/max, pattern, email, etc.)
5//! - Custom validator support
6//! - Field-level and form-level validation
7//! - Validation state management
8
9use std::collections::HashMap;
10use std::fmt;
11
12/// Validation result for a single field.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ValidationResult {
15    /// Validation passed.
16    Valid,
17    /// Validation failed with an error message.
18    Invalid(String),
19    /// Validation is pending (e.g., async validation).
20    Pending,
21}
22
23impl ValidationResult {
24    /// Check if validation passed.
25    pub fn is_valid(&self) -> bool {
26        matches!(self, Self::Valid)
27    }
28
29    /// Check if validation failed.
30    pub fn is_invalid(&self) -> bool {
31        matches!(self, Self::Invalid(_))
32    }
33
34    /// Check if validation is pending.
35    pub fn is_pending(&self) -> bool {
36        matches!(self, Self::Pending)
37    }
38
39    /// Get the error message if invalid.
40    pub fn error(&self) -> Option<&str> {
41        match self {
42            Self::Invalid(msg) => Some(msg),
43            _ => None,
44        }
45    }
46}
47
48/// A validator that can validate a string value.
49pub trait Validator: Send + Sync {
50    /// Validate the given value.
51    fn validate(&self, value: &str) -> ValidationResult;
52
53    /// Get the name of this validator.
54    fn name(&self) -> &str;
55}
56
57/// Required field validator.
58#[derive(Debug, Clone)]
59pub struct Required {
60    message: String,
61}
62
63impl Required {
64    /// Create a required validator with default message.
65    pub fn new() -> Self {
66        Self {
67            message: "This field is required".to_string(),
68        }
69    }
70
71    /// Create with custom message.
72    pub fn with_message(message: &str) -> Self {
73        Self {
74            message: message.to_string(),
75        }
76    }
77}
78
79impl Default for Required {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl Validator for Required {
86    fn validate(&self, value: &str) -> ValidationResult {
87        if value.trim().is_empty() {
88            ValidationResult::Invalid(self.message.clone())
89        } else {
90            ValidationResult::Valid
91        }
92    }
93
94    fn name(&self) -> &'static str {
95        "required"
96    }
97}
98
99/// Minimum length validator.
100#[derive(Debug, Clone)]
101pub struct MinLength {
102    min: usize,
103    message: String,
104}
105
106impl MinLength {
107    /// Create a min length validator.
108    pub fn new(min: usize) -> Self {
109        Self {
110            min,
111            message: format!("Must be at least {min} characters"),
112        }
113    }
114
115    /// Create with custom message.
116    pub fn with_message(min: usize, message: &str) -> Self {
117        Self {
118            min,
119            message: message.to_string(),
120        }
121    }
122}
123
124impl Validator for MinLength {
125    fn validate(&self, value: &str) -> ValidationResult {
126        if value.chars().count() < self.min {
127            ValidationResult::Invalid(self.message.clone())
128        } else {
129            ValidationResult::Valid
130        }
131    }
132
133    fn name(&self) -> &'static str {
134        "minLength"
135    }
136}
137
138/// Maximum length validator.
139#[derive(Debug, Clone)]
140pub struct MaxLength {
141    max: usize,
142    message: String,
143}
144
145impl MaxLength {
146    /// Create a max length validator.
147    pub fn new(max: usize) -> Self {
148        Self {
149            max,
150            message: format!("Must be at most {max} characters"),
151        }
152    }
153
154    /// Create with custom message.
155    pub fn with_message(max: usize, message: &str) -> Self {
156        Self {
157            max,
158            message: message.to_string(),
159        }
160    }
161}
162
163impl Validator for MaxLength {
164    fn validate(&self, value: &str) -> ValidationResult {
165        if value.chars().count() > self.max {
166            ValidationResult::Invalid(self.message.clone())
167        } else {
168            ValidationResult::Valid
169        }
170    }
171
172    fn name(&self) -> &'static str {
173        "maxLength"
174    }
175}
176
177/// Range validator for numeric values.
178#[derive(Debug, Clone)]
179pub struct Range {
180    min: f64,
181    max: f64,
182    message: String,
183}
184
185impl Range {
186    /// Create a range validator.
187    pub fn new(min: f64, max: f64) -> Self {
188        Self {
189            min,
190            max,
191            message: format!("Must be between {min} and {max}"),
192        }
193    }
194
195    /// Create with custom message.
196    pub fn with_message(min: f64, max: f64, message: &str) -> Self {
197        Self {
198            min,
199            max,
200            message: message.to_string(),
201        }
202    }
203}
204
205impl Validator for Range {
206    fn validate(&self, value: &str) -> ValidationResult {
207        match value.parse::<f64>() {
208            Ok(num) if num >= self.min && num <= self.max => ValidationResult::Valid,
209            Ok(_) => ValidationResult::Invalid(self.message.clone()),
210            Err(_) => ValidationResult::Invalid("Must be a valid number".to_string()),
211        }
212    }
213
214    fn name(&self) -> &'static str {
215        "range"
216    }
217}
218
219/// Pattern validator using regex-like patterns.
220/// Note: Uses simple pattern matching, not full regex.
221#[derive(Debug, Clone)]
222pub struct Pattern {
223    pattern: PatternType,
224    message: String,
225}
226
227/// Type of pattern to match.
228#[derive(Debug, Clone)]
229pub enum PatternType {
230    /// Email address pattern.
231    Email,
232    /// URL pattern.
233    Url,
234    /// Phone number (digits, spaces, dashes, parens).
235    Phone,
236    /// Alphanumeric only.
237    Alphanumeric,
238    /// Digits only.
239    Digits,
240    /// Custom pattern (simple glob-style).
241    Custom(String),
242}
243
244impl Pattern {
245    /// Create an email validator.
246    pub fn email() -> Self {
247        Self {
248            pattern: PatternType::Email,
249            message: "Must be a valid email address".to_string(),
250        }
251    }
252
253    /// Create a URL validator.
254    pub fn url() -> Self {
255        Self {
256            pattern: PatternType::Url,
257            message: "Must be a valid URL".to_string(),
258        }
259    }
260
261    /// Create a phone validator.
262    pub fn phone() -> Self {
263        Self {
264            pattern: PatternType::Phone,
265            message: "Must be a valid phone number".to_string(),
266        }
267    }
268
269    /// Create an alphanumeric validator.
270    pub fn alphanumeric() -> Self {
271        Self {
272            pattern: PatternType::Alphanumeric,
273            message: "Must contain only letters and numbers".to_string(),
274        }
275    }
276
277    /// Create a digits-only validator.
278    pub fn digits() -> Self {
279        Self {
280            pattern: PatternType::Digits,
281            message: "Must contain only digits".to_string(),
282        }
283    }
284
285    /// Create with custom message.
286    pub fn with_message(mut self, message: &str) -> Self {
287        self.message = message.to_string();
288        self
289    }
290
291    fn matches(&self, value: &str) -> bool {
292        match &self.pattern {
293            PatternType::Email => {
294                // Simple email validation: has @ and . after @
295                let parts: Vec<&str> = value.split('@').collect();
296                parts.len() == 2
297                    && !parts[0].is_empty()
298                    && parts[1].contains('.')
299                    && !parts[1].starts_with('.')
300                    && !parts[1].ends_with('.')
301            }
302            PatternType::Url => {
303                value.starts_with("http://")
304                    || value.starts_with("https://")
305                    || value.starts_with("ftp://")
306            }
307            PatternType::Phone => {
308                value.chars().all(|c| {
309                    c.is_ascii_digit() || c == ' ' || c == '-' || c == '(' || c == ')' || c == '+'
310                }) && value.chars().filter(char::is_ascii_digit).count() >= 7
311            }
312            PatternType::Alphanumeric => value.chars().all(char::is_alphanumeric),
313            PatternType::Digits => value.chars().all(|c| c.is_ascii_digit()),
314            PatternType::Custom(pattern) => {
315                // Simple glob matching (* = any chars)
316                if pattern.is_empty() {
317                    return true;
318                }
319                let parts: Vec<&str> = pattern.split('*').collect();
320                if parts.len() == 1 {
321                    return value == pattern;
322                }
323                let mut remaining = value;
324                for (i, part) in parts.iter().enumerate() {
325                    if part.is_empty() {
326                        continue;
327                    }
328                    if i == 0 {
329                        if !remaining.starts_with(part) {
330                            return false;
331                        }
332                        remaining = &remaining[part.len()..];
333                    } else if i == parts.len() - 1 {
334                        if !remaining.ends_with(part) {
335                            return false;
336                        }
337                    } else if let Some(pos) = remaining.find(part) {
338                        remaining = &remaining[pos + part.len()..];
339                    } else {
340                        return false;
341                    }
342                }
343                true
344            }
345        }
346    }
347}
348
349impl Validator for Pattern {
350    fn validate(&self, value: &str) -> ValidationResult {
351        if value.is_empty() {
352            // Empty values are handled by Required validator
353            return ValidationResult::Valid;
354        }
355
356        if self.matches(value) {
357            ValidationResult::Valid
358        } else {
359            ValidationResult::Invalid(self.message.clone())
360        }
361    }
362
363    fn name(&self) -> &'static str {
364        "pattern"
365    }
366}
367
368/// Custom function validator.
369pub struct Custom<F>
370where
371    F: Fn(&str) -> ValidationResult + Send + Sync,
372{
373    validator: F,
374    name: String,
375}
376
377impl<F> Custom<F>
378where
379    F: Fn(&str) -> ValidationResult + Send + Sync,
380{
381    /// Create a custom validator.
382    pub fn new(name: &str, validator: F) -> Self {
383        Self {
384            validator,
385            name: name.to_string(),
386        }
387    }
388}
389
390impl<F> Validator for Custom<F>
391where
392    F: Fn(&str) -> ValidationResult + Send + Sync,
393{
394    fn validate(&self, value: &str) -> ValidationResult {
395        (self.validator)(value)
396    }
397
398    fn name(&self) -> &str {
399        &self.name
400    }
401}
402
403impl<F> fmt::Debug for Custom<F>
404where
405    F: Fn(&str) -> ValidationResult + Send + Sync,
406{
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        f.debug_struct("Custom").field("name", &self.name).finish()
409    }
410}
411
412/// Field validation state.
413#[derive(Debug, Clone, Default)]
414pub struct FieldState {
415    /// Current value.
416    pub value: String,
417    /// Validation result.
418    pub result: Option<ValidationResult>,
419    /// Whether the field has been touched (focused then blurred).
420    pub touched: bool,
421    /// Whether the field has been modified.
422    pub dirty: bool,
423    /// Field-specific errors (from validators).
424    pub errors: Vec<String>,
425}
426
427impl FieldState {
428    /// Create a new field state.
429    pub fn new() -> Self {
430        Self::default()
431    }
432
433    /// Create with initial value.
434    pub fn with_value(value: &str) -> Self {
435        Self {
436            value: value.to_string(),
437            ..Default::default()
438        }
439    }
440
441    /// Check if field is valid.
442    pub fn is_valid(&self) -> bool {
443        self.result
444            .as_ref()
445            .map_or(true, ValidationResult::is_valid)
446    }
447
448    /// Check if field has errors.
449    pub fn has_errors(&self) -> bool {
450        !self.errors.is_empty()
451    }
452
453    /// Get first error message.
454    pub fn first_error(&self) -> Option<&str> {
455        self.errors.first().map(std::string::String::as_str)
456    }
457
458    /// Mark as touched.
459    pub fn touch(&mut self) {
460        self.touched = true;
461    }
462
463    /// Update value.
464    pub fn set_value(&mut self, value: &str) {
465        if self.value != value {
466            self.value = value.to_string();
467            self.dirty = true;
468        }
469    }
470}
471
472/// Configuration for a validated field.
473#[derive(Default)]
474pub struct FieldConfig {
475    /// Validators for this field.
476    validators: Vec<Box<dyn Validator>>,
477    /// When to validate.
478    validate_on: ValidateOn,
479}
480
481impl std::fmt::Debug for FieldConfig {
482    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483        f.debug_struct("FieldConfig")
484            .field("validator_count", &self.validators.len())
485            .field("validate_on", &self.validate_on)
486            .finish()
487    }
488}
489
490/// When to run validation.
491#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
492pub enum ValidateOn {
493    /// Validate on value change.
494    #[default]
495    Change,
496    /// Validate on blur.
497    Blur,
498    /// Validate only on submit.
499    Submit,
500}
501
502impl FieldConfig {
503    /// Create a new field config.
504    pub fn new() -> Self {
505        Self::default()
506    }
507
508    /// Add a validator.
509    pub fn add_validator<V: Validator + 'static>(mut self, validator: V) -> Self {
510        self.validators.push(Box::new(validator));
511        self
512    }
513
514    /// Add required validator.
515    pub fn required(self) -> Self {
516        self.add_validator(Required::new())
517    }
518
519    /// Add min length validator.
520    pub fn min_length(self, min: usize) -> Self {
521        self.add_validator(MinLength::new(min))
522    }
523
524    /// Add max length validator.
525    pub fn max_length(self, max: usize) -> Self {
526        self.add_validator(MaxLength::new(max))
527    }
528
529    /// Add range validator.
530    pub fn range(self, min: f64, max: f64) -> Self {
531        self.add_validator(Range::new(min, max))
532    }
533
534    /// Add email pattern validator.
535    pub fn email(self) -> Self {
536        self.add_validator(Pattern::email())
537    }
538
539    /// Set validation trigger.
540    pub fn validate_on(mut self, trigger: ValidateOn) -> Self {
541        self.validate_on = trigger;
542        self
543    }
544
545    /// Run all validators on a value.
546    pub fn validate(&self, value: &str) -> Vec<String> {
547        let mut errors = Vec::new();
548        for validator in &self.validators {
549            if let ValidationResult::Invalid(msg) = validator.validate(value) {
550                errors.push(msg);
551            }
552        }
553        errors
554    }
555}
556
557/// Form validation state manager.
558#[derive(Debug, Default)]
559pub struct FormValidator {
560    /// Field configurations.
561    configs: HashMap<String, FieldConfig>,
562    /// Field states.
563    states: HashMap<String, FieldState>,
564    /// Whether the form has been submitted.
565    submitted: bool,
566}
567
568impl FormValidator {
569    /// Create a new form validator.
570    pub fn new() -> Self {
571        Self::default()
572    }
573
574    /// Register a field with configuration.
575    pub fn register(&mut self, name: &str, config: FieldConfig) {
576        self.configs.insert(name.to_string(), config);
577        self.states.insert(name.to_string(), FieldState::new());
578    }
579
580    /// Register a field with default config.
581    pub fn register_field(&mut self, name: &str) {
582        self.register(name, FieldConfig::new());
583    }
584
585    /// Set field value.
586    pub fn set_value(&mut self, name: &str, value: &str) {
587        if let Some(state) = self.states.get_mut(name) {
588            state.set_value(value);
589
590            // Check if we should validate on change
591            if let Some(config) = self.configs.get(name) {
592                if config.validate_on == ValidateOn::Change {
593                    state.errors = config.validate(value);
594                    state.result = if state.errors.is_empty() {
595                        Some(ValidationResult::Valid)
596                    } else {
597                        Some(ValidationResult::Invalid(state.errors[0].clone()))
598                    };
599                }
600            }
601        }
602    }
603
604    /// Mark field as touched (for blur validation).
605    pub fn touch(&mut self, name: &str) {
606        if let Some(state) = self.states.get_mut(name) {
607            state.touch();
608
609            // Check if we should validate on blur
610            if let Some(config) = self.configs.get(name) {
611                if config.validate_on == ValidateOn::Blur {
612                    state.errors = config.validate(&state.value);
613                    state.result = if state.errors.is_empty() {
614                        Some(ValidationResult::Valid)
615                    } else {
616                        Some(ValidationResult::Invalid(state.errors[0].clone()))
617                    };
618                }
619            }
620        }
621    }
622
623    /// Get field state.
624    pub fn field(&self, name: &str) -> Option<&FieldState> {
625        self.states.get(name)
626    }
627
628    /// Get field value.
629    pub fn value(&self, name: &str) -> Option<&str> {
630        self.states.get(name).map(|s| s.value.as_str())
631    }
632
633    /// Get field errors.
634    pub fn errors(&self, name: &str) -> &[String] {
635        self.states.get(name).map_or(&[], |s| s.errors.as_slice())
636    }
637
638    /// Check if a specific field is valid.
639    pub fn field_is_valid(&self, name: &str) -> bool {
640        self.states.get(name).is_some_and(FieldState::is_valid)
641    }
642
643    /// Validate all fields and return overall validity.
644    pub fn validate(&mut self) -> bool {
645        let mut all_valid = true;
646
647        for (name, config) in &self.configs {
648            if let Some(state) = self.states.get_mut(name) {
649                state.errors = config.validate(&state.value);
650                state.result = if state.errors.is_empty() {
651                    Some(ValidationResult::Valid)
652                } else {
653                    Some(ValidationResult::Invalid(state.errors[0].clone()))
654                };
655
656                if !state.errors.is_empty() {
657                    all_valid = false;
658                }
659            }
660        }
661
662        self.submitted = true;
663        all_valid
664    }
665
666    /// Check if form is valid (all fields pass validation).
667    pub fn is_valid(&self) -> bool {
668        self.states.values().all(FieldState::is_valid)
669    }
670
671    /// Check if form has been submitted.
672    pub fn is_submitted(&self) -> bool {
673        self.submitted
674    }
675
676    /// Check if any field is dirty.
677    pub fn is_dirty(&self) -> bool {
678        self.states.values().any(|s| s.dirty)
679    }
680
681    /// Get all validation errors as a map.
682    pub fn all_errors(&self) -> HashMap<&str, &[String]> {
683        self.states
684            .iter()
685            .filter(|(_, s)| !s.errors.is_empty())
686            .map(|(k, s)| (k.as_str(), s.errors.as_slice()))
687            .collect()
688    }
689
690    /// Reset all field states.
691    pub fn reset(&mut self) {
692        for state in self.states.values_mut() {
693            state.value.clear();
694            state.result = None;
695            state.touched = false;
696            state.dirty = false;
697            state.errors.clear();
698        }
699        self.submitted = false;
700    }
701
702    /// Get field count.
703    pub fn field_count(&self) -> usize {
704        self.configs.len()
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711
712    // ValidationResult tests
713    #[test]
714    fn test_validation_result_valid() {
715        let result = ValidationResult::Valid;
716        assert!(result.is_valid());
717        assert!(!result.is_invalid());
718        assert!(!result.is_pending());
719        assert!(result.error().is_none());
720    }
721
722    #[test]
723    fn test_validation_result_invalid() {
724        let result = ValidationResult::Invalid("Error message".to_string());
725        assert!(!result.is_valid());
726        assert!(result.is_invalid());
727        assert!(!result.is_pending());
728        assert_eq!(result.error(), Some("Error message"));
729    }
730
731    #[test]
732    fn test_validation_result_pending() {
733        let result = ValidationResult::Pending;
734        assert!(!result.is_valid());
735        assert!(!result.is_invalid());
736        assert!(result.is_pending());
737    }
738
739    // Required validator tests
740    #[test]
741    fn test_required_validator() {
742        let validator = Required::new();
743        assert_eq!(validator.name(), "required");
744
745        assert!(validator.validate("hello").is_valid());
746        assert!(validator.validate("  content  ").is_valid());
747        assert!(validator.validate("").is_invalid());
748        assert!(validator.validate("   ").is_invalid());
749    }
750
751    #[test]
752    fn test_required_custom_message() {
753        let validator = Required::with_message("Custom error");
754        let result = validator.validate("");
755        assert_eq!(result.error(), Some("Custom error"));
756    }
757
758    // MinLength validator tests
759    #[test]
760    fn test_min_length_validator() {
761        let validator = MinLength::new(3);
762        assert_eq!(validator.name(), "minLength");
763
764        assert!(validator.validate("abc").is_valid());
765        assert!(validator.validate("abcd").is_valid());
766        assert!(validator.validate("ab").is_invalid());
767        assert!(validator.validate("").is_invalid());
768    }
769
770    #[test]
771    fn test_min_length_unicode() {
772        let validator = MinLength::new(3);
773        assert!(validator.validate("日本語").is_valid()); // 3 characters
774        assert!(validator.validate("日本").is_invalid()); // 2 characters
775    }
776
777    // MaxLength validator tests
778    #[test]
779    fn test_max_length_validator() {
780        let validator = MaxLength::new(5);
781        assert_eq!(validator.name(), "maxLength");
782
783        assert!(validator.validate("").is_valid());
784        assert!(validator.validate("abc").is_valid());
785        assert!(validator.validate("abcde").is_valid());
786        assert!(validator.validate("abcdef").is_invalid());
787    }
788
789    // Range validator tests
790    #[test]
791    fn test_range_validator() {
792        let validator = Range::new(0.0, 100.0);
793        assert_eq!(validator.name(), "range");
794
795        assert!(validator.validate("0").is_valid());
796        assert!(validator.validate("50").is_valid());
797        assert!(validator.validate("100").is_valid());
798        assert!(validator.validate("-1").is_invalid());
799        assert!(validator.validate("101").is_invalid());
800    }
801
802    #[test]
803    fn test_range_with_decimals() {
804        let validator = Range::new(0.0, 1.0);
805
806        assert!(validator.validate("0.5").is_valid());
807        assert!(validator.validate("0.99").is_valid());
808        assert!(validator.validate("1.01").is_invalid());
809    }
810
811    #[test]
812    fn test_range_invalid_number() {
813        let validator = Range::new(0.0, 100.0);
814        let result = validator.validate("not a number");
815        assert!(result.is_invalid());
816        assert_eq!(result.error(), Some("Must be a valid number"));
817    }
818
819    // Pattern validator tests
820    #[test]
821    fn test_pattern_email() {
822        let validator = Pattern::email();
823
824        assert!(validator.validate("test@example.com").is_valid());
825        assert!(validator.validate("user.name@domain.co.uk").is_valid());
826        assert!(validator.validate("").is_valid()); // Empty handled by Required
827
828        assert!(validator.validate("invalid").is_invalid());
829        assert!(validator.validate("@missing.com").is_invalid());
830        assert!(validator.validate("missing@").is_invalid());
831        assert!(validator.validate("missing@.com").is_invalid());
832    }
833
834    #[test]
835    fn test_pattern_url() {
836        let validator = Pattern::url();
837
838        assert!(validator.validate("http://example.com").is_valid());
839        assert!(validator.validate("https://example.com").is_valid());
840        assert!(validator.validate("ftp://files.example.com").is_valid());
841
842        assert!(validator.validate("example.com").is_invalid());
843        assert!(validator.validate("www.example.com").is_invalid());
844    }
845
846    #[test]
847    fn test_pattern_phone() {
848        let validator = Pattern::phone();
849
850        assert!(validator.validate("1234567").is_valid());
851        assert!(validator.validate("123-456-7890").is_valid());
852        assert!(validator.validate("(123) 456-7890").is_valid());
853        assert!(validator.validate("+1 234 567 8900").is_valid());
854
855        assert!(validator.validate("123").is_invalid()); // Too short
856        assert!(validator.validate("abc-def-ghij").is_invalid());
857    }
858
859    #[test]
860    fn test_pattern_alphanumeric() {
861        let validator = Pattern::alphanumeric();
862
863        assert!(validator.validate("abc123").is_valid());
864        assert!(validator.validate("ABC").is_valid());
865        assert!(validator.validate("123").is_valid());
866
867        assert!(validator.validate("abc-123").is_invalid());
868        assert!(validator.validate("hello world").is_invalid());
869    }
870
871    #[test]
872    fn test_pattern_digits() {
873        let validator = Pattern::digits();
874
875        assert!(validator.validate("123456").is_valid());
876        assert!(validator.validate("0").is_valid());
877
878        assert!(validator.validate("123a").is_invalid());
879        assert!(validator.validate("12.34").is_invalid());
880    }
881
882    // Custom validator tests
883    #[test]
884    fn test_custom_validator() {
885        let validator = Custom::new("even_length", |value| {
886            if value.len() % 2 == 0 {
887                ValidationResult::Valid
888            } else {
889                ValidationResult::Invalid("Length must be even".to_string())
890            }
891        });
892
893        assert_eq!(validator.name(), "even_length");
894        assert!(validator.validate("ab").is_valid());
895        assert!(validator.validate("abcd").is_valid());
896        assert!(validator.validate("abc").is_invalid());
897    }
898
899    // FieldState tests
900    #[test]
901    fn test_field_state_new() {
902        let state = FieldState::new();
903        assert!(state.value.is_empty());
904        assert!(!state.touched);
905        assert!(!state.dirty);
906        assert!(state.errors.is_empty());
907    }
908
909    #[test]
910    fn test_field_state_with_value() {
911        let state = FieldState::with_value("initial");
912        assert_eq!(state.value, "initial");
913    }
914
915    #[test]
916    fn test_field_state_touch() {
917        let mut state = FieldState::new();
918        assert!(!state.touched);
919        state.touch();
920        assert!(state.touched);
921    }
922
923    #[test]
924    fn test_field_state_set_value() {
925        let mut state = FieldState::new();
926        assert!(!state.dirty);
927        state.set_value("new value");
928        assert!(state.dirty);
929        assert_eq!(state.value, "new value");
930    }
931
932    #[test]
933    fn test_field_state_first_error() {
934        let mut state = FieldState::new();
935        assert!(state.first_error().is_none());
936
937        state.errors.push("First error".to_string());
938        state.errors.push("Second error".to_string());
939        assert_eq!(state.first_error(), Some("First error"));
940    }
941
942    // FieldConfig tests
943    #[test]
944    fn test_field_config_builder() {
945        let config = FieldConfig::new()
946            .required()
947            .min_length(3)
948            .max_length(10)
949            .email();
950
951        assert_eq!(config.validators.len(), 4);
952    }
953
954    #[test]
955    fn test_field_config_validate() {
956        let config = FieldConfig::new().required().min_length(3);
957
958        let errors = config.validate("");
959        assert_eq!(errors.len(), 2); // Required and MinLength
960
961        let errors = config.validate("ab");
962        assert_eq!(errors.len(), 1); // Only MinLength
963
964        let errors = config.validate("abc");
965        assert!(errors.is_empty());
966    }
967
968    // FormValidator tests
969    #[test]
970    fn test_form_validator_new() {
971        let form = FormValidator::new();
972        assert_eq!(form.field_count(), 0);
973        assert!(!form.is_submitted());
974    }
975
976    #[test]
977    fn test_form_validator_register() {
978        let mut form = FormValidator::new();
979        form.register("email", FieldConfig::new().required().email());
980        form.register("name", FieldConfig::new().required());
981
982        assert_eq!(form.field_count(), 2);
983    }
984
985    #[test]
986    fn test_form_validator_set_value() {
987        let mut form = FormValidator::new();
988        form.register("name", FieldConfig::new());
989
990        form.set_value("name", "John");
991        assert_eq!(form.value("name"), Some("John"));
992        assert!(form.is_dirty());
993    }
994
995    #[test]
996    fn test_form_validator_validate_on_change() {
997        let mut form = FormValidator::new();
998        form.register(
999            "email",
1000            FieldConfig::new()
1001                .required()
1002                .email()
1003                .validate_on(ValidateOn::Change),
1004        );
1005
1006        form.set_value("email", "invalid");
1007        assert!(!form.field_is_valid("email"));
1008
1009        form.set_value("email", "valid@example.com");
1010        assert!(form.field_is_valid("email"));
1011    }
1012
1013    #[test]
1014    fn test_form_validator_validate_on_blur() {
1015        let mut form = FormValidator::new();
1016        form.register(
1017            "email",
1018            FieldConfig::new().required().validate_on(ValidateOn::Blur),
1019        );
1020
1021        form.set_value("email", "");
1022        // Should not validate yet
1023        assert!(form.errors("email").is_empty());
1024
1025        form.touch("email");
1026        // Now should validate
1027        assert!(!form.errors("email").is_empty());
1028    }
1029
1030    #[test]
1031    fn test_form_validator_validate_all() {
1032        let mut form = FormValidator::new();
1033        form.register("name", FieldConfig::new().required());
1034        form.register("email", FieldConfig::new().required().email());
1035
1036        form.set_value("name", "John");
1037        form.set_value("email", "invalid");
1038
1039        let valid = form.validate();
1040        assert!(!valid);
1041        assert!(form.is_submitted());
1042
1043        let errors = form.all_errors();
1044        assert_eq!(errors.len(), 1); // Only email has errors
1045    }
1046
1047    #[test]
1048    fn test_form_validator_reset() {
1049        let mut form = FormValidator::new();
1050        form.register("name", FieldConfig::new().required());
1051
1052        form.set_value("name", "John");
1053        form.touch("name");
1054        form.validate();
1055
1056        form.reset();
1057
1058        assert!(!form.is_submitted());
1059        assert!(!form.is_dirty());
1060        assert_eq!(form.value("name"), Some(""));
1061    }
1062
1063    #[test]
1064    fn test_form_validator_is_valid() {
1065        let mut form = FormValidator::new();
1066        form.register("name", FieldConfig::new().required());
1067
1068        form.set_value("name", "");
1069        form.validate();
1070        assert!(!form.is_valid());
1071
1072        form.set_value("name", "John");
1073        form.validate();
1074        assert!(form.is_valid());
1075    }
1076
1077    // =========================================================================
1078    // ValidationResult Additional Tests
1079    // =========================================================================
1080
1081    #[test]
1082    fn test_validation_result_clone() {
1083        let result = ValidationResult::Invalid("error".to_string());
1084        let cloned = result.clone();
1085        assert_eq!(result, cloned);
1086    }
1087
1088    #[test]
1089    fn test_validation_result_debug() {
1090        let result = ValidationResult::Valid;
1091        let debug = format!("{:?}", result);
1092        assert!(debug.contains("Valid"));
1093    }
1094
1095    #[test]
1096    fn test_validation_result_partial_eq() {
1097        assert_eq!(ValidationResult::Valid, ValidationResult::Valid);
1098        assert_eq!(ValidationResult::Pending, ValidationResult::Pending);
1099        assert_ne!(ValidationResult::Valid, ValidationResult::Pending);
1100        assert_ne!(
1101            ValidationResult::Invalid("a".to_string()),
1102            ValidationResult::Invalid("b".to_string())
1103        );
1104    }
1105
1106    // =========================================================================
1107    // Required Validator Edge Cases
1108    // =========================================================================
1109
1110    #[test]
1111    fn test_required_default() {
1112        let validator = Required::default();
1113        assert!(validator.validate("").is_invalid());
1114    }
1115
1116    #[test]
1117    fn test_required_whitespace_only() {
1118        let validator = Required::new();
1119        assert!(validator.validate("\t\n").is_invalid());
1120        assert!(validator.validate("  \t  ").is_invalid());
1121    }
1122
1123    #[test]
1124    fn test_required_single_char() {
1125        let validator = Required::new();
1126        assert!(validator.validate("a").is_valid());
1127        assert!(validator.validate(" a ").is_valid());
1128    }
1129
1130    #[test]
1131    fn test_required_clone() {
1132        let validator = Required::with_message("custom");
1133        let cloned = validator.clone();
1134        assert_eq!(cloned.message, "custom");
1135    }
1136
1137    // =========================================================================
1138    // MinLength Boundary Tests
1139    // =========================================================================
1140
1141    #[test]
1142    fn test_min_length_boundary_zero() {
1143        let validator = MinLength::new(0);
1144        assert!(validator.validate("").is_valid());
1145        assert!(validator.validate("a").is_valid());
1146    }
1147
1148    #[test]
1149    fn test_min_length_boundary_one() {
1150        let validator = MinLength::new(1);
1151        assert!(validator.validate("").is_invalid());
1152        assert!(validator.validate("a").is_valid());
1153        assert!(validator.validate("ab").is_valid());
1154    }
1155
1156    #[test]
1157    fn test_min_length_boundary_exact() {
1158        let validator = MinLength::new(5);
1159        assert!(validator.validate("1234").is_invalid()); // min-1
1160        assert!(validator.validate("12345").is_valid()); // exact min
1161        assert!(validator.validate("123456").is_valid()); // min+1
1162    }
1163
1164    #[test]
1165    fn test_min_length_custom_message() {
1166        let validator = MinLength::with_message(3, "Too short!");
1167        let result = validator.validate("ab");
1168        assert_eq!(result.error(), Some("Too short!"));
1169    }
1170
1171    #[test]
1172    fn test_min_length_clone() {
1173        let validator = MinLength::new(5);
1174        let cloned = validator.clone();
1175        assert_eq!(cloned.min, 5);
1176    }
1177
1178    // =========================================================================
1179    // MaxLength Boundary Tests
1180    // =========================================================================
1181
1182    #[test]
1183    fn test_max_length_boundary_zero() {
1184        let validator = MaxLength::new(0);
1185        assert!(validator.validate("").is_valid());
1186        assert!(validator.validate("a").is_invalid());
1187    }
1188
1189    #[test]
1190    fn test_max_length_boundary_one() {
1191        let validator = MaxLength::new(1);
1192        assert!(validator.validate("").is_valid());
1193        assert!(validator.validate("a").is_valid());
1194        assert!(validator.validate("ab").is_invalid());
1195    }
1196
1197    #[test]
1198    fn test_max_length_boundary_exact() {
1199        let validator = MaxLength::new(5);
1200        assert!(validator.validate("1234").is_valid()); // max-1
1201        assert!(validator.validate("12345").is_valid()); // exact max
1202        assert!(validator.validate("123456").is_invalid()); // max+1
1203    }
1204
1205    #[test]
1206    fn test_max_length_custom_message() {
1207        let validator = MaxLength::with_message(3, "Too long!");
1208        let result = validator.validate("abcd");
1209        assert_eq!(result.error(), Some("Too long!"));
1210    }
1211
1212    #[test]
1213    fn test_max_length_unicode() {
1214        let validator = MaxLength::new(3);
1215        assert!(validator.validate("日本語").is_valid()); // 3 chars
1216        assert!(validator.validate("日本語字").is_invalid()); // 4 chars
1217    }
1218
1219    #[test]
1220    fn test_max_length_clone() {
1221        let validator = MaxLength::new(10);
1222        let cloned = validator.clone();
1223        assert_eq!(cloned.max, 10);
1224    }
1225
1226    // =========================================================================
1227    // Range Validator Edge Cases
1228    // =========================================================================
1229
1230    #[test]
1231    fn test_range_boundary_exact() {
1232        let validator = Range::new(10.0, 20.0);
1233        assert!(validator.validate("9.99").is_invalid());
1234        assert!(validator.validate("10.0").is_valid());
1235        assert!(validator.validate("20.0").is_valid());
1236        assert!(validator.validate("20.01").is_invalid());
1237    }
1238
1239    #[test]
1240    fn test_range_negative_values() {
1241        let validator = Range::new(-100.0, -10.0);
1242        assert!(validator.validate("-50").is_valid());
1243        assert!(validator.validate("-100").is_valid());
1244        assert!(validator.validate("-10").is_valid());
1245        assert!(validator.validate("-101").is_invalid());
1246        assert!(validator.validate("-9").is_invalid());
1247    }
1248
1249    #[test]
1250    fn test_range_scientific_notation() {
1251        let validator = Range::new(0.0, 1e10);
1252        assert!(validator.validate("1e5").is_valid());
1253        assert!(validator.validate("1E9").is_valid());
1254        assert!(validator.validate("1e11").is_invalid());
1255    }
1256
1257    #[test]
1258    fn test_range_float_precision() {
1259        let validator = Range::new(0.1, 0.3);
1260        assert!(validator.validate("0.2").is_valid());
1261        // Edge case: floating point representation
1262        assert!(validator.validate("0.1").is_valid());
1263        assert!(validator.validate("0.3").is_valid());
1264    }
1265
1266    #[test]
1267    fn test_range_custom_message() {
1268        let validator = Range::with_message(1.0, 10.0, "Out of range!");
1269        let result = validator.validate("0");
1270        assert_eq!(result.error(), Some("Out of range!"));
1271    }
1272
1273    #[test]
1274    fn test_range_clone() {
1275        let validator = Range::new(0.0, 100.0);
1276        let cloned = validator.clone();
1277        assert_eq!(cloned.min, 0.0);
1278        assert_eq!(cloned.max, 100.0);
1279    }
1280
1281    #[test]
1282    fn test_range_empty_string() {
1283        let validator = Range::new(0.0, 100.0);
1284        let result = validator.validate("");
1285        assert!(result.is_invalid());
1286        assert_eq!(result.error(), Some("Must be a valid number"));
1287    }
1288
1289    // =========================================================================
1290    // Pattern Validator Edge Cases
1291    // =========================================================================
1292
1293    #[test]
1294    fn test_pattern_email_edge_cases() {
1295        let validator = Pattern::email();
1296
1297        // Valid edge cases
1298        assert!(validator.validate("a@b.c").is_valid());
1299        assert!(validator.validate("test+tag@example.com").is_valid());
1300        assert!(validator.validate("test.name@sub.domain.com").is_valid());
1301
1302        // Invalid edge cases
1303        assert!(validator.validate("test@@example.com").is_invalid());
1304        assert!(validator.validate("test@.example.com").is_invalid());
1305        assert!(validator.validate("test@example.").is_invalid());
1306    }
1307
1308    #[test]
1309    fn test_pattern_url_protocols() {
1310        let validator = Pattern::url();
1311
1312        assert!(validator.validate("http://localhost").is_valid());
1313        assert!(validator.validate("https://127.0.0.1").is_valid());
1314        assert!(validator.validate("ftp://ftp.example.com").is_valid());
1315
1316        assert!(validator.validate("file://local").is_invalid());
1317        assert!(validator.validate("mailto:test@example.com").is_invalid());
1318    }
1319
1320    #[test]
1321    fn test_pattern_phone_international() {
1322        let validator = Pattern::phone();
1323
1324        assert!(validator.validate("+44 20 7946 0958").is_valid());
1325        assert!(validator.validate("+1-800-555-1234").is_valid());
1326        assert!(validator.validate("(555) 123-4567").is_valid());
1327    }
1328
1329    #[test]
1330    fn test_pattern_phone_too_short() {
1331        let validator = Pattern::phone();
1332        assert!(validator.validate("123456").is_invalid()); // < 7 digits
1333    }
1334
1335    #[test]
1336    fn test_pattern_alphanumeric_unicode() {
1337        let validator = Pattern::alphanumeric();
1338        // Unicode letters should be valid
1339        assert!(validator.validate("日本語").is_valid());
1340        assert!(validator.validate("Café").is_valid());
1341    }
1342
1343    #[test]
1344    fn test_pattern_digits_edge_cases() {
1345        let validator = Pattern::digits();
1346
1347        assert!(validator.validate("0").is_valid());
1348        assert!(validator.validate("0123456789").is_valid());
1349
1350        assert!(validator.validate("-1").is_invalid());
1351        assert!(validator.validate("+1").is_invalid());
1352        assert!(validator.validate("1.0").is_invalid());
1353    }
1354
1355    #[test]
1356    fn test_pattern_custom_message() {
1357        let validator = Pattern::email().with_message("Invalid email format");
1358        let result = validator.validate("invalid");
1359        assert_eq!(result.error(), Some("Invalid email format"));
1360    }
1361
1362    #[test]
1363    fn test_pattern_clone() {
1364        let validator = Pattern::email();
1365        let cloned = validator.clone();
1366        assert!(cloned.validate("test@example.com").is_valid());
1367    }
1368
1369    #[test]
1370    fn test_pattern_custom_glob_exact() {
1371        let pattern = Pattern {
1372            pattern: PatternType::Custom("hello".to_string()),
1373            message: "Must be hello".to_string(),
1374        };
1375        assert!(pattern.validate("hello").is_valid());
1376        assert!(pattern.validate("hello!").is_invalid());
1377        assert!(pattern.validate("hellp").is_invalid());
1378    }
1379
1380    #[test]
1381    fn test_pattern_custom_glob_prefix() {
1382        let pattern = Pattern {
1383            pattern: PatternType::Custom("test*".to_string()),
1384            message: "Must start with test".to_string(),
1385        };
1386        assert!(pattern.validate("test").is_valid());
1387        assert!(pattern.validate("testing").is_valid());
1388        assert!(pattern.validate("TEST").is_invalid());
1389    }
1390
1391    #[test]
1392    fn test_pattern_custom_glob_suffix() {
1393        let pattern = Pattern {
1394            pattern: PatternType::Custom("*.txt".to_string()),
1395            message: "Must end with .txt".to_string(),
1396        };
1397        assert!(pattern.validate("file.txt").is_valid());
1398        assert!(pattern.validate(".txt").is_valid());
1399        assert!(pattern.validate("file.doc").is_invalid());
1400    }
1401
1402    #[test]
1403    fn test_pattern_custom_glob_middle() {
1404        let pattern = Pattern {
1405            pattern: PatternType::Custom("pre*suf".to_string()),
1406            message: "error".to_string(),
1407        };
1408        assert!(pattern.validate("presuf").is_valid());
1409        assert!(pattern.validate("pre123suf").is_valid());
1410        assert!(pattern.validate("prefix_suffix").is_invalid());
1411    }
1412
1413    #[test]
1414    fn test_pattern_custom_glob_empty() {
1415        let pattern = Pattern {
1416            pattern: PatternType::Custom("".to_string()),
1417            message: "error".to_string(),
1418        };
1419        // Empty pattern matches anything
1420        assert!(pattern.validate("anything").is_valid());
1421        assert!(pattern.validate("").is_valid());
1422    }
1423
1424    #[test]
1425    fn test_pattern_custom_glob_multiple_wildcards() {
1426        let pattern = Pattern {
1427            pattern: PatternType::Custom("a*b*c".to_string()),
1428            message: "error".to_string(),
1429        };
1430        assert!(pattern.validate("abc").is_valid());
1431        assert!(pattern.validate("a123b456c").is_valid());
1432        assert!(pattern.validate("axbxc").is_valid());
1433        assert!(pattern.validate("axbx").is_invalid());
1434    }
1435
1436    // =========================================================================
1437    // Custom Validator Tests
1438    // =========================================================================
1439
1440    #[test]
1441    fn test_custom_validator_debug() {
1442        let validator = Custom::new("test", |_| ValidationResult::Valid);
1443        let debug = format!("{:?}", validator);
1444        assert!(debug.contains("Custom"));
1445        assert!(debug.contains("test"));
1446    }
1447
1448    #[test]
1449    fn test_custom_validator_pending() {
1450        let validator = Custom::new("async_check", |_| ValidationResult::Pending);
1451        assert!(validator.validate("anything").is_pending());
1452    }
1453
1454    // =========================================================================
1455    // FieldState Additional Tests
1456    // =========================================================================
1457
1458    #[test]
1459    fn test_field_state_is_valid_no_result() {
1460        let state = FieldState::new();
1461        assert!(state.is_valid()); // No result means valid
1462    }
1463
1464    #[test]
1465    fn test_field_state_is_valid_with_result() {
1466        let mut state = FieldState::new();
1467        state.result = Some(ValidationResult::Invalid("error".to_string()));
1468        assert!(!state.is_valid());
1469
1470        state.result = Some(ValidationResult::Valid);
1471        assert!(state.is_valid());
1472    }
1473
1474    #[test]
1475    fn test_field_state_has_errors() {
1476        let mut state = FieldState::new();
1477        assert!(!state.has_errors());
1478
1479        state.errors.push("error".to_string());
1480        assert!(state.has_errors());
1481    }
1482
1483    #[test]
1484    fn test_field_state_set_value_same_value() {
1485        let mut state = FieldState::with_value("test");
1486        state.set_value("test"); // Same value
1487        assert!(!state.dirty); // Should not be dirty
1488    }
1489
1490    #[test]
1491    fn test_field_state_default() {
1492        let state = FieldState::default();
1493        assert!(state.value.is_empty());
1494        assert!(state.result.is_none());
1495        assert!(!state.touched);
1496        assert!(!state.dirty);
1497        assert!(state.errors.is_empty());
1498    }
1499
1500    #[test]
1501    fn test_field_state_clone() {
1502        let mut state = FieldState::with_value("test");
1503        state.touched = true;
1504        state.dirty = true;
1505        state.errors.push("error".to_string());
1506
1507        let cloned = state.clone();
1508        assert_eq!(cloned.value, "test");
1509        assert!(cloned.touched);
1510        assert!(cloned.dirty);
1511        assert_eq!(cloned.errors.len(), 1);
1512    }
1513
1514    // =========================================================================
1515    // FieldConfig Additional Tests
1516    // =========================================================================
1517
1518    #[test]
1519    fn test_field_config_validate_on() {
1520        let config = FieldConfig::new().validate_on(ValidateOn::Blur);
1521        assert_eq!(config.validate_on, ValidateOn::Blur);
1522    }
1523
1524    #[test]
1525    fn test_field_config_range() {
1526        let config = FieldConfig::new().range(0.0, 100.0);
1527
1528        let errors = config.validate("50");
1529        assert!(errors.is_empty());
1530
1531        let errors = config.validate("150");
1532        assert!(!errors.is_empty());
1533    }
1534
1535    #[test]
1536    fn test_field_config_debug() {
1537        let config = FieldConfig::new().required().min_length(3);
1538        let debug = format!("{:?}", config);
1539        assert!(debug.contains("FieldConfig"));
1540        assert!(debug.contains("validator_count"));
1541    }
1542
1543    #[test]
1544    fn test_field_config_default() {
1545        let config = FieldConfig::default();
1546        assert!(config.validators.is_empty());
1547        assert_eq!(config.validate_on, ValidateOn::Change);
1548    }
1549
1550    #[test]
1551    fn test_field_config_multiple_validators_all_fail() {
1552        let config = FieldConfig::new().required().min_length(5).max_length(3); // Impossible constraint
1553
1554        let errors = config.validate("");
1555        assert_eq!(errors.len(), 2); // Required and MinLength
1556    }
1557
1558    // =========================================================================
1559    // ValidateOn Tests
1560    // =========================================================================
1561
1562    #[test]
1563    fn test_validate_on_default() {
1564        assert_eq!(ValidateOn::default(), ValidateOn::Change);
1565    }
1566
1567    #[test]
1568    fn test_validate_on_debug() {
1569        let trigger = ValidateOn::Submit;
1570        let debug = format!("{:?}", trigger);
1571        assert!(debug.contains("Submit"));
1572    }
1573
1574    #[test]
1575    fn test_validate_on_clone() {
1576        let trigger = ValidateOn::Blur;
1577        let cloned = trigger;
1578        assert_eq!(trigger, cloned);
1579    }
1580
1581    // =========================================================================
1582    // FormValidator Additional Tests
1583    // =========================================================================
1584
1585    #[test]
1586    fn test_form_validator_register_field() {
1587        let mut form = FormValidator::new();
1588        form.register_field("simple");
1589        assert_eq!(form.field_count(), 1);
1590        assert!(form.field("simple").is_some());
1591    }
1592
1593    #[test]
1594    fn test_form_validator_field_nonexistent() {
1595        let form = FormValidator::new();
1596        assert!(form.field("nonexistent").is_none());
1597        assert!(form.value("nonexistent").is_none());
1598    }
1599
1600    #[test]
1601    fn test_form_validator_errors_nonexistent() {
1602        let form = FormValidator::new();
1603        assert!(form.errors("nonexistent").is_empty());
1604    }
1605
1606    #[test]
1607    fn test_form_validator_set_value_nonexistent() {
1608        let mut form = FormValidator::new();
1609        form.set_value("nonexistent", "value"); // Should not panic
1610    }
1611
1612    #[test]
1613    fn test_form_validator_touch_nonexistent() {
1614        let mut form = FormValidator::new();
1615        form.touch("nonexistent"); // Should not panic
1616    }
1617
1618    #[test]
1619    fn test_form_validator_field_is_valid_nonexistent() {
1620        let form = FormValidator::new();
1621        assert!(!form.field_is_valid("nonexistent")); // Non-existent is not valid
1622    }
1623
1624    #[test]
1625    fn test_form_validator_validate_submit_only() {
1626        let mut form = FormValidator::new();
1627        form.register(
1628            "field",
1629            FieldConfig::new()
1630                .required()
1631                .validate_on(ValidateOn::Submit),
1632        );
1633
1634        form.set_value("field", "");
1635        // Should not validate on change
1636        assert!(form.errors("field").is_empty());
1637
1638        form.touch("field");
1639        // Should not validate on blur
1640        assert!(form.errors("field").is_empty());
1641
1642        form.validate();
1643        // Should validate on submit
1644        assert!(!form.errors("field").is_empty());
1645    }
1646
1647    #[test]
1648    fn test_form_validator_all_errors_empty() {
1649        let mut form = FormValidator::new();
1650        form.register("valid", FieldConfig::new().required());
1651        form.set_value("valid", "value");
1652        form.validate();
1653
1654        let errors = form.all_errors();
1655        assert!(errors.is_empty());
1656    }
1657
1658    #[test]
1659    fn test_form_validator_all_errors_multiple() {
1660        let mut form = FormValidator::new();
1661        form.register("field1", FieldConfig::new().required());
1662        form.register("field2", FieldConfig::new().required());
1663        form.validate();
1664
1665        let errors = form.all_errors();
1666        assert_eq!(errors.len(), 2);
1667    }
1668
1669    #[test]
1670    fn test_form_validator_default() {
1671        let form = FormValidator::default();
1672        assert_eq!(form.field_count(), 0);
1673    }
1674
1675    #[test]
1676    fn test_form_validator_debug() {
1677        let form = FormValidator::new();
1678        let debug = format!("{:?}", form);
1679        assert!(debug.contains("FormValidator"));
1680    }
1681
1682    #[test]
1683    fn test_form_validator_is_dirty_multiple_fields() {
1684        let mut form = FormValidator::new();
1685        form.register_field("a");
1686        form.register_field("b");
1687
1688        assert!(!form.is_dirty());
1689
1690        form.set_value("a", "value");
1691        assert!(form.is_dirty());
1692    }
1693
1694    #[test]
1695    fn test_form_validator_reset_clears_all() {
1696        let mut form = FormValidator::new();
1697        form.register("a", FieldConfig::new().required());
1698        form.register("b", FieldConfig::new().required());
1699
1700        form.set_value("a", "value1");
1701        form.set_value("b", "value2");
1702        form.touch("a");
1703        form.validate();
1704
1705        form.reset();
1706
1707        assert!(!form.is_submitted());
1708        assert!(!form.is_dirty());
1709        assert_eq!(form.value("a"), Some(""));
1710        assert_eq!(form.value("b"), Some(""));
1711    }
1712
1713    #[test]
1714    fn test_form_validator_validate_returns_true_when_valid() {
1715        let mut form = FormValidator::new();
1716        form.register("name", FieldConfig::new().required());
1717        form.set_value("name", "John");
1718
1719        assert!(form.validate());
1720    }
1721
1722    #[test]
1723    fn test_form_validator_complex_scenario() {
1724        let mut form = FormValidator::new();
1725
1726        form.register(
1727            "email",
1728            FieldConfig::new()
1729                .required()
1730                .email()
1731                .validate_on(ValidateOn::Change),
1732        );
1733
1734        form.register(
1735            "password",
1736            FieldConfig::new()
1737                .required()
1738                .min_length(8)
1739                .validate_on(ValidateOn::Blur),
1740        );
1741
1742        form.register(
1743            "age",
1744            FieldConfig::new()
1745                .range(18.0, 120.0)
1746                .validate_on(ValidateOn::Submit),
1747        );
1748
1749        // Fill email - validates immediately
1750        form.set_value("email", "invalid");
1751        assert!(!form.field_is_valid("email"));
1752
1753        form.set_value("email", "test@example.com");
1754        assert!(form.field_is_valid("email"));
1755
1756        // Fill password - doesn't validate until blur
1757        form.set_value("password", "short");
1758        assert!(form.errors("password").is_empty());
1759
1760        form.touch("password");
1761        assert!(!form.errors("password").is_empty());
1762
1763        form.set_value("password", "longpassword123");
1764        form.touch("password");
1765        assert!(form.errors("password").is_empty());
1766
1767        // Fill age - doesn't validate until submit
1768        form.set_value("age", "15");
1769        assert!(form.errors("age").is_empty());
1770
1771        // Submit validates everything
1772        assert!(!form.validate()); // Age is out of range
1773
1774        form.set_value("age", "25");
1775        assert!(form.validate());
1776        assert!(form.is_valid());
1777    }
1778}