1use crate::parse_result::{ErrorCollector, ParserConfig};
21
22pub trait Validate {
24 fn validate(&self, path: &str, config: &ParserConfig, collector: &mut ErrorCollector);
26}
27
28pub mod helpers {
30 use crate::error::ValidationError;
31 use crate::parse_result::{ErrorCollector, ParserConfig};
32 use regex::Regex;
33
34 pub fn validate_length(
36 value: &str,
37 field_name: &str,
38 min: Option<usize>,
39 max: Option<usize>,
40 path: &str,
41 config: &ParserConfig,
42 collector: &mut ErrorCollector,
43 ) -> bool {
44 let mut valid = true;
45
46 if let Some(min_len) = min
47 && value.chars().count() < min_len
48 {
49 let error = ValidationError::new(
50 1001,
51 format!("{field_name} is shorter than the minimum length of {min_len}"),
52 )
53 .with_field(field_name.to_string())
54 .with_path(path.to_string());
55
56 if config.fail_fast {
57 collector.add_critical_error(error);
58 return false;
59 } else {
60 collector.add_error(error);
61 valid = false;
62 }
63 }
64
65 if let Some(max_len) = max
66 && value.chars().count() > max_len
67 {
68 let error = ValidationError::new(
69 1002,
70 format!("{field_name} exceeds the maximum length of {max_len}"),
71 )
72 .with_field(field_name.to_string())
73 .with_path(path.to_string());
74
75 if config.fail_fast {
76 collector.add_critical_error(error);
77 return false;
78 } else {
79 collector.add_error(error);
80 valid = false;
81 }
82 }
83
84 valid
85 }
86
87 pub fn validate_pattern(
89 value: &str,
90 field_name: &str,
91 pattern: &str,
92 path: &str,
93 config: &ParserConfig,
94 collector: &mut ErrorCollector,
95 ) -> bool {
96 let trimmed_value = value.trim();
98
99 let regex = match Regex::new(pattern) {
100 Ok(r) => r,
101 Err(_) => {
102 collector.add_critical_error(
103 ValidationError::new(
104 9999,
105 format!("Invalid regex pattern for {field_name}: {pattern}"),
106 )
107 .with_field(field_name.to_string())
108 .with_path(path.to_string()),
109 );
110 return false;
111 }
112 };
113
114 if !regex.is_match(trimmed_value) {
115 let error = ValidationError::new(
116 1005,
117 format!("{field_name} does not match the required pattern (value: '{value}')"),
118 )
119 .with_field(field_name.to_string())
120 .with_path(path.to_string());
121
122 if config.fail_fast {
123 collector.add_critical_error(error);
124 return false;
125 } else {
126 collector.add_error(error);
127 return false;
128 }
129 }
130
131 true
132 }
133
134 pub fn validate_required<T>(
136 value: &Option<T>,
137 field_name: &str,
138 path: &str,
139 _config: &ParserConfig,
140 collector: &mut ErrorCollector,
141 ) -> bool {
142 if value.is_none() {
143 let error = ValidationError::new(1003, format!("{field_name} is required"))
144 .with_field(field_name.to_string())
145 .with_path(path.to_string());
146
147 collector.add_critical_error(error);
148 return false;
149 }
150 true
151 }
152
153 pub fn child_path(parent: &str, field: &str) -> String {
155 if parent.is_empty() {
156 field.to_string()
157 } else {
158 format!("{parent}.{field}")
159 }
160 }
161}