1#![allow(clippy::use_debug, clippy::pattern_type_mismatch)]
5
6use crate::*;
7
8type String = Rc<str>;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum ValidationError {
13 TypeMismatch {
15 expected: String,
16 actual: String,
17 path: String,
18 },
19 OutOfRange {
21 value: String,
22 min: Option<String>,
23 max: Option<String>,
24 path: String,
25 },
26 LengthConstraint {
28 actual_length: usize,
29 min_length: Option<usize>,
30 max_length: Option<usize>,
31 path: String,
32 },
33 PatternMismatch {
35 value: String,
36 pattern: String,
37 path: String,
38 },
39 ArraySizeConstraint {
41 actual_size: usize,
42 min_items: Option<usize>,
43 max_items: Option<usize>,
44 path: String,
45 },
46 MissingRequiredProperty { property: String, path: String },
48 PropertyValidationFailed {
50 property: String,
51 path: String,
52 error: Box<ValidationError>,
53 },
54 AdditionalPropertiesNotAllowed { property: String, path: String },
56 NotInEnum {
58 value: String,
59 allowed_values: Vec<String>,
60 path: String,
61 },
62 ConstMismatch {
64 expected: String,
65 actual: String,
66 path: String,
67 },
68 NoUnionMatch {
70 path: String,
71 errors: Vec<ValidationError>,
72 },
73 InvalidPattern { pattern: String, error: String },
75 ArrayItemValidationFailed {
77 index: usize,
78 path: String,
79 error: Box<ValidationError>,
80 },
81 NonStringKey { key_type: String, path: String },
83 MissingDiscriminator { discriminator: String, path: String },
85 UnknownDiscriminatorValue {
87 discriminator: String,
88 value: String,
89 allowed_values: Vec<String>,
90 path: String,
91 },
92 DiscriminatedSubobjectValidationFailed {
94 discriminator: String,
95 value: String,
96 path: String,
97 error: Box<ValidationError>,
98 },
99}
100
101impl fmt::Display for ValidationError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 ValidationError::TypeMismatch {
105 expected,
106 actual,
107 path,
108 } => {
109 write!(
110 f,
111 "Type mismatch at '{path}': expected {expected}, got {actual}"
112 )
113 }
114 ValidationError::OutOfRange {
115 value,
116 min,
117 max,
118 path,
119 } => {
120 let range_desc = match (min, max) {
121 (Some(min), Some(max)) => format!("between {min} and {max}"),
122 (Some(min), None) => format!("at least {min}"),
123 (None, Some(max)) => format!("at most {max}"),
124 (None, None) => "within valid range".to_string(),
125 };
126 write!(
127 f,
128 "Value {value} at '{path}' is out of range: must be {range_desc}"
129 )
130 }
131 ValidationError::LengthConstraint {
132 actual_length,
133 min_length,
134 max_length,
135 path,
136 } => {
137 let constraint_desc = match (min_length, max_length) {
138 (Some(min), Some(max)) => format!("between {min} and {max} characters"),
139 (Some(min), None) => format!("at least {min} characters"),
140 (None, Some(max)) => format!("at most {max} characters"),
141 (None, None) => "within valid length".to_string(),
142 };
143 write!(
144 f,
145 "String length {actual_length} at '{path}' violates constraint: must be {constraint_desc}"
146 )
147 }
148 ValidationError::PatternMismatch {
149 value,
150 pattern,
151 path,
152 } => {
153 write!(
154 f,
155 "String '{value}' at '{path}' does not match pattern '{pattern}'"
156 )
157 }
158 ValidationError::ArraySizeConstraint {
159 actual_size,
160 min_items,
161 max_items,
162 path,
163 } => {
164 let constraint_desc = match (min_items, max_items) {
165 (Some(min), Some(max)) => format!("between {min} and {max} items"),
166 (Some(min), None) => format!("at least {min} items"),
167 (None, Some(max)) => format!("at most {max} items"),
168 (None, None) => "within valid size".to_string(),
169 };
170 write!(
171 f,
172 "Array size {actual_size} at '{path}' violates constraint: must have {constraint_desc}"
173 )
174 }
175 ValidationError::MissingRequiredProperty { property, path } => {
176 write!(f, "Missing required property '{property}' at '{path}'")
177 }
178 ValidationError::PropertyValidationFailed {
179 property,
180 path,
181 error,
182 } => {
183 write!(
184 f,
185 "Property '{property}' at '{path}' failed validation: {error}"
186 )
187 }
188 ValidationError::AdditionalPropertiesNotAllowed { property, path } => {
189 write!(
190 f,
191 "Additional property '{property}' not allowed at '{path}'"
192 )
193 }
194 ValidationError::NotInEnum {
195 value,
196 allowed_values,
197 path,
198 } => {
199 let values_json = serde_json::to_string(&allowed_values)
200 .unwrap_or_else(|_| format!("{allowed_values:?}"));
201
202 write!(
203 f,
204 "Value '{value}' at '{path}' is not in allowed enum values: {values_json}",
205 )
206 }
207 ValidationError::ConstMismatch {
208 expected,
209 actual,
210 path,
211 } => {
212 write!(
213 f,
214 "Constant mismatch at '{path}': expected '{expected}', got '{actual}'"
215 )
216 }
217 ValidationError::NoUnionMatch { path, errors } => {
218 write!(
219 f,
220 "Value at '{path}' does not match any schema in union. Errors: {errors:?}"
221 )
222 }
223 ValidationError::InvalidPattern { pattern, error } => {
224 write!(f, "Invalid regex pattern '{pattern}': {error}")
225 }
226 ValidationError::ArrayItemValidationFailed { index, path, error } => {
227 write!(
228 f,
229 "Array item {index} at '{path}' failed validation: {error}"
230 )
231 }
232 ValidationError::NonStringKey { key_type, path } => {
233 write!(
234 f,
235 "Object key at '{path}' must be a string, but found {key_type}"
236 )
237 }
238 ValidationError::MissingDiscriminator {
239 discriminator,
240 path,
241 } => {
242 write!(
243 f,
244 "Missing discriminator field '{discriminator}' at '{path}'"
245 )
246 }
247 ValidationError::UnknownDiscriminatorValue {
248 discriminator,
249 value,
250 allowed_values,
251 path,
252 } => {
253 let values_json: Vec<serde_json::Value> = allowed_values
254 .iter()
255 .map(|v| serde_json::Value::String(v.to_string()))
256 .collect();
257 write!(
258 f,
259 "Unknown discriminator value '{value}' for field '{discriminator}' at '{path}'. Allowed values: {}",
260 serde_json::to_string(&values_json).unwrap_or_else(|_| format!("{values_json:?}"))
261 )
262 }
263 ValidationError::DiscriminatedSubobjectValidationFailed {
264 discriminator,
265 value,
266 path,
267 error,
268 } => {
269 write!(
270 f,
271 "Discriminated subobject validation failed for discriminator '{discriminator}' with value '{value}' at '{path}': {error}"
272 )
273 }
274 }
275 }
276}
277
278impl core::error::Error for ValidationError {}