satay_codegen/error/validation.rs
1/// Errors that can occur while validating an OpenAPI document.
2///
3/// This enum is [`non_exhaustive`](https://doc.rust-lang.org/reference/attributes/type_system.html)
4/// so new variants may be added in future releases without a semver break.
5#[derive(Debug, thiserror::Error)]
6#[non_exhaustive]
7pub enum ValidationError {
8 // -- Document and component shape validation --
9 /// The OpenAPI version is not supported.
10 ///
11 /// Error message: `unsupported OpenAPI version `{version}`; Satay supports OpenAPI 3.1`
12 #[error("unsupported OpenAPI version `{version}`; Satay supports OpenAPI 3.1")]
13 UnsupportedOpenApiVersion { version: String },
14
15 /// A schema component uses a type that is not supported.
16 ///
17 /// Error message: `unsupported type `{kind}` in schema `{schema}``
18 #[error("unsupported type `{kind}` in schema `{schema}`")]
19 UnsupportedComponentType { schema: String, kind: String },
20
21 /// A schema component is missing a required `type`, `$ref`, `enum`, or `properties` declaration.
22 ///
23 /// Error message: `schema `{schema}` must declare `type`, `$ref`, `enum`, or `properties``
24 #[error("schema `{schema}` must declare `type`, `$ref`, `enum`, or `properties`")]
25 MissingComponentSchemaType { schema: String },
26
27 /// An object schema is missing the required `properties` field.
28 ///
29 /// Error message: `object schema `{schema}` must declare `properties``
30 #[error("object schema `{schema}` must declare `properties`")]
31 MissingObjectProperties { schema: String },
32
33 /// The OpenAPI document is missing the required `paths` field.
34 ///
35 /// Error message: `OpenAPI document must declare `paths``
36 #[error("OpenAPI document must declare `paths`")]
37 MissingPaths,
38
39 // -- Enum and schema type validation --
40 /// A schema uses an enum with a non-string type.
41 ///
42 /// Error message: `{context} uses enum type `{kind}`; only string enums are supported`
43 #[error("{context} uses enum type `{kind}`; only string enums are supported")]
44 UnsupportedEnumType { context: String, kind: String },
45
46 /// A schema declares an enum that is not an array.
47 ///
48 /// Error message: `{context} has a non-array enum`
49 #[error("{context} has a non-array enum")]
50 NonArrayEnum { context: String },
51
52 /// A schema declares an enum with no values.
53 ///
54 /// Error message: `{context} has an empty enum`
55 #[error("{context} has an empty enum")]
56 EmptyEnum { context: String },
57
58 /// A schema enum contains a non-string value.
59 ///
60 /// Error message: `{context} contains a non-string enum value; only string enums are supported`
61 #[error("{context} contains a non-string enum value; only string enums are supported")]
62 NonStringEnumValue { context: String },
63
64 /// A schema declares a `const` value that is not one of its `enum` values.
65 ///
66 /// Error message: `{context} declares a `const` value that is not in its `enum``
67 #[error("{context} declares a `const` value that is not in its `enum`")]
68 ConstNotInEnum { context: String },
69
70 /// An `x-satay.enum-variants` value is not an object.
71 ///
72 /// Error message: `{context}.x-satay.enum-variants must be an object`
73 #[error("{context}.x-satay.enum-variants must be an object")]
74 InvalidSatayEnumVariants { context: String },
75
76 /// An `x-satay.enum-variants` entry points at a value that is not in the enum.
77 ///
78 /// Error message: `{context}.x-satay.enum-variants contains `{wire_name}`, which is not declared in the enum`
79 #[error(
80 "{context}.x-satay.enum-variants contains `{wire_name}`, which is not declared in the enum"
81 )]
82 UnknownSatayEnumVariantValue { context: String, wire_name: String },
83
84 /// An `x-satay.enum-variants` entry has a non-string Rust variant name.
85 ///
86 /// Error message: `{context}.x-satay.enum-variants[{wire_name:?}] must be a string`
87 #[error("{context}.x-satay.enum-variants[{wire_name:?}] must be a string")]
88 InvalidSatayEnumVariantName { context: String, wire_name: String },
89
90 /// An `x-satay.enum-variants` entry uses a name reserved for generated fallback variants.
91 ///
92 /// Error message: `{context}.x-satay.enum-variants[{wire_name:?}] uses reserved fallback variant `{rust_name}``
93 #[error(
94 "{context}.x-satay.enum-variants[{wire_name:?}] uses reserved fallback variant `{rust_name}`"
95 )]
96 ReservedSatayEnumVariantName {
97 context: String,
98 wire_name: String,
99 rust_name: String,
100 },
101
102 /// Two `x-satay.enum-variants` entries produce the same Rust variant name.
103 ///
104 /// Error message: `{context}.x-satay.enum-variants maps multiple values to `{rust_name}``
105 #[error("{context}.x-satay.enum-variants maps multiple values to `{rust_name}`")]
106 DuplicateSatayEnumVariantName { context: String, rust_name: String },
107
108 /// A schema has a `required` field that is not an array.
109 ///
110 /// Error message: `{context} has a non-array `required` field`
111 #[error("{context} has a non-array `required` field")]
112 NonArrayRequired { context: String },
113
114 /// A schema `required` array contains a non-string element.
115 ///
116 /// Error message: `{context} has a non-string required field name`
117 #[error("{context} has a non-string required field name")]
118 NonStringRequiredField { context: String },
119
120 /// An integer schema uses an unsupported format.
121 ///
122 /// Error message: `{context} uses unsupported integer format `{format}``
123 #[error("{context} uses unsupported integer format `{format}`")]
124 UnsupportedIntegerFormat { context: String, format: String },
125
126 /// A number schema uses an unsupported format.
127 ///
128 /// Error message: `{context} uses unsupported number format `{format}``
129 #[error("{context} uses unsupported number format `{format}`")]
130 UnsupportedNumberFormat { context: String, format: String },
131
132 /// An `x-satay.parse-as` value is not a supported target type.
133 ///
134 /// Error message: `{context} uses unsupported x-satay.parse-as `{parse_as}``
135 #[error("{context} uses unsupported x-satay.parse-as `{parse_as}`")]
136 UnsupportedSatayParseAs { context: String, parse_as: String },
137
138 /// An `x-satay.parse-as` value is not a string.
139 ///
140 /// Error message: `{context}.x-satay.parse-as must be a string`
141 #[error("{context}.x-satay.parse-as must be a string")]
142 InvalidSatayParseAs { context: String },
143
144 /// `x-satay.parse-as` was applied to an unsupported wire schema.
145 ///
146 /// Error message: `{context} uses x-satay.parse-as `{parse_as}` on `{kind}`; supported parse-as wire schemas are string schemas, plus integer schemas for bool`
147 #[error(
148 "{context} uses x-satay.parse-as `{parse_as}` on `{kind}`; supported parse-as wire schemas are string schemas, plus integer schemas for bool"
149 )]
150 SatayParseAsRequiresString {
151 context: String,
152 parse_as: String,
153 kind: String,
154 },
155
156 /// An `x-satay.none-if` value is not an array.
157 ///
158 /// Error message: `{context}.x-satay.none-if must be an array`
159 #[error("{context}.x-satay.none-if must be an array")]
160 InvalidSatayNoneIf { context: String },
161
162 /// An `x-satay.none-if` array is empty.
163 ///
164 /// Error message: `{context}.x-satay.none-if must contain at least one string`
165 #[error("{context}.x-satay.none-if must contain at least one string")]
166 EmptySatayNoneIf { context: String },
167
168 /// An `x-satay.none-if` entry is not a string.
169 ///
170 /// Error message: `{context}.x-satay.none-if values must be strings`
171 #[error("{context}.x-satay.none-if values must be strings")]
172 InvalidSatayNoneIfValue { context: String },
173
174 /// `x-satay.none-if` was applied outside a struct property.
175 ///
176 /// Error message: `{context} uses x-satay.none-if outside a struct field`
177 #[error("{context} uses x-satay.none-if outside a struct field")]
178 SatayNoneIfRequiresStructField { context: String },
179
180 /// `x-satay.none-if` was not paired with a string-backed parser.
181 ///
182 /// Error message: `{context} uses x-satay.none-if without a string-backed x-satay.parse-as`
183 #[error("{context} uses x-satay.none-if without a string-backed x-satay.parse-as")]
184 SatayNoneIfRequiresParsedString { context: String },
185
186 /// `x-satay.none-if` and `x-satay.treat-error-as-none` were combined.
187 ///
188 /// Error message: `{context} cannot combine x-satay.none-if with x-satay.treat-error-as-none`
189 #[error("{context} cannot combine x-satay.none-if with x-satay.treat-error-as-none")]
190 ConflictingSatayNoneHandling { context: String },
191
192 /// An `x-satay.integer-type` value is not a supported Rust integer type.
193 ///
194 /// Error message: `{context} uses unsupported x-satay.integer-type `{integer_type}``
195 #[error("{context} uses unsupported x-satay.integer-type `{integer_type}`")]
196 UnsupportedSatayIntegerType {
197 context: String,
198 integer_type: String,
199 },
200
201 /// An `x-satay.integer-type` value is not a string.
202 ///
203 /// Error message: `{context}.x-satay.integer-type must be a string`
204 #[error("{context}.x-satay.integer-type must be a string")]
205 InvalidSatayIntegerType { context: String },
206
207 /// `x-satay.integer-type` was applied to a non-integer schema.
208 ///
209 /// Error message: `{context} uses x-satay.integer-type `{integer_type}` on `{kind}`; supported integer-type wire schemas are integer schemas and string schemas with x-satay.parse-as integer-range`
210 #[error(
211 "{context} uses x-satay.integer-type `{integer_type}` on `{kind}`; supported integer-type wire schemas are integer schemas and string schemas with x-satay.parse-as integer-range"
212 )]
213 SatayIntegerTypeRequiresInteger {
214 context: String,
215 integer_type: String,
216 kind: String,
217 },
218
219 /// An array schema is missing the required `items` field.
220 ///
221 /// Error message: `{context} array schema must declare `items``
222 #[error("{context} array schema must declare `items`")]
223 MissingArrayItems { context: String },
224
225 /// A schema defines an inline object instead of using a `$ref`.
226 ///
227 /// Error message: `{context} is an inline object schema; move it to components/schemas and use `$ref``
228 #[error("{context} is an inline object schema; move it to components/schemas and use `$ref`")]
229 InlineObjectSchema { context: String },
230
231 /// An object schema has no properties (i.e. acts as a map/dictionary), which is unsupported.
232 ///
233 /// Error message: `{context} is an object with neither `properties` nor a supported `additionalProperties` schema`
234 #[error(
235 "{context} is an object with neither `properties` nor a supported `additionalProperties` schema"
236 )]
237 UnsupportedMapObjectSchema { context: String },
238
239 /// A schema uses an unsupported type.
240 ///
241 /// Error message: `{context} uses unsupported schema type `{kind}``
242 #[error("{context} uses unsupported schema type `{kind}`")]
243 UnsupportedSchemaType { context: String, kind: String },
244
245 /// A schema is missing a required `type`, `$ref`, or `enum` declaration.
246 ///
247 /// Error message: `{context} must declare `type`, `$ref`, or `enum``
248 #[error("{context} must declare `type`, `$ref`, or `enum`")]
249 MissingSchemaType { context: String },
250
251 /// A JSON Schema boolean schema was used; Satay has no IR equivalent yet.
252 ///
253 /// Error message: `{context} is a boolean schema; boolean JSON Schemas are not supported yet`
254 #[error("{context} is a boolean schema; boolean JSON Schemas are not supported yet")]
255 UnsupportedBooleanSchema { context: String },
256
257 /// A schema type array contains more than one non-null type.
258 ///
259 /// Error message: `{context} declares multiple non-null schema types; Satay supports at most one plus null`
260 #[error(
261 "{context} declares multiple non-null schema types; Satay supports at most one plus null"
262 )]
263 MultipleNonNullSchemaTypesUnsupported { context: String },
264
265 /// A schema uses a composition keyword (`allOf`, `anyOf`, `oneOf`) in an unsupported context.
266 ///
267 /// Error message: `{context} uses `{keyword}`, which is not supported in this context`
268 #[error("{context} uses `{keyword}`, which is not supported in this context")]
269 UnsupportedComposition {
270 context: String,
271 keyword: &'static str,
272 },
273
274 /// An `allOf` schema combines supported struct flattening with another schema keyword.
275 ///
276 /// Error message: `{context} uses `allOf` with `{keyword}`; only object branch flattening is supported`
277 #[error("{context} uses `allOf` with `{keyword}`; only object branch flattening is supported")]
278 UnsupportedAllOfSiblingKeyword { context: String, keyword: String },
279
280 /// An `allOf` branch cannot be flattened into a generated Rust struct.
281 ///
282 /// Error message: `{context}.allOf[{index}] must be a local component schema reference or object schema with properties`
283 #[error(
284 "{context}.allOf[{index}] must be a local component schema reference or object schema with properties"
285 )]
286 UnsupportedAllOfBranch { context: String, index: usize },
287
288 /// Two `allOf` branches declare the same object property.
289 ///
290 /// Error message: `{context} declares duplicate `allOf` property `{property}``
291 #[error("{context} declares duplicate `allOf` property `{property}`")]
292 DuplicateAllOfProperty { context: String, property: String },
293
294 /// `allOf` component schemas form a recursive flattening cycle.
295 ///
296 /// Error message: `{context} forms a recursive `allOf` cycle through schema `{schema}``
297 #[error("{context} forms a recursive `allOf` cycle through schema `{schema}`")]
298 RecursiveAllOf { context: String, schema: String },
299
300 /// A discriminator union branch component recursively contains its own union.
301 ///
302 /// Error message: `{context} forms a recursive discriminator cycle through branch schema `{schema}``
303 #[error("{context} forms a recursive discriminator cycle through branch schema `{schema}`")]
304 RecursiveDiscriminatorBranch { context: String, schema: String },
305
306 /// An `anyOf` schema combines a supported union with another schema keyword.
307 ///
308 /// Error message: `{context} uses `anyOf` with `{keyword}`; only annotation siblings are supported`
309 #[error("{context} uses `anyOf` with `{keyword}`; only annotation siblings are supported")]
310 UnsupportedAnyOfSiblingKeyword { context: String, keyword: String },
311
312 /// An `anyOf` branch is not a supported union branch.
313 ///
314 /// Error message: `{context}.anyOf[{index}] must be a local component schema reference, inline string enum, inline primitive schema, or null schema`
315 #[error(
316 "{context}.anyOf[{index}] must be a local component schema reference, inline string enum, inline primitive schema, or null schema"
317 )]
318 UnsupportedAnyOfBranch { context: String, index: usize },
319
320 /// A `oneOf` schema combines a supported union with another schema keyword.
321 ///
322 /// Error message: `{context} uses `oneOf` with `{keyword}`; only annotation siblings are supported`
323 #[error("{context} uses `oneOf` with `{keyword}`; only annotation siblings are supported")]
324 UnsupportedOneOfSiblingKeyword { context: String, keyword: String },
325
326 /// A `oneOf` branch is not a supported union branch.
327 ///
328 /// Error message: `{context}.oneOf[{index}] must be a local component schema reference, inline string enum, inline primitive schema, or null schema`
329 #[error(
330 "{context}.oneOf[{index}] must be a local component schema reference, inline string enum, inline primitive schema, or null schema"
331 )]
332 UnsupportedOneOfBranch { context: String, index: usize },
333
334 /// A plain `anyOf` or `oneOf` union has more than one null branch.
335 ///
336 /// Error message: `{context}.{keyword}[{index}] duplicates the union null branch`
337 #[error("{context}.{keyword}[{index}] duplicates the union null branch")]
338 DuplicateUnionNullBranch {
339 context: String,
340 keyword: &'static str,
341 index: usize,
342 },
343
344 /// An open string enum `anyOf` repeats an enum or `const` value across branches.
345 ///
346 /// Error message: `{context} declares duplicate open string enum value `{value}` across `anyOf` branches`
347 #[error(
348 "{context} declares duplicate open string enum value `{value}` across `anyOf` branches"
349 )]
350 DuplicateOpenStringEnumValue { context: String, value: String },
351
352 /// A nullable plain `anyOf` or `oneOf` union has no non-null branches.
353 ///
354 /// Error message: `{context}.{keyword} must declare at least one non-null branch`
355 #[error("{context}.{keyword} must declare at least one non-null branch")]
356 NullableUnionWithoutVariants {
357 context: String,
358 keyword: &'static str,
359 },
360
361 /// A plain `anyOf` or `oneOf` union has a branch that is statically shadowed by an earlier branch.
362 ///
363 /// Error message: `{context}.{keyword}[{index}] is shadowed by earlier branch {shadowed_by} under ordered serde untagged deserialization`
364 #[error(
365 "{context}.{keyword}[{index}] is shadowed by earlier branch {shadowed_by} under ordered serde untagged deserialization"
366 )]
367 ShadowedUnionBranch {
368 context: String,
369 keyword: &'static str,
370 index: usize,
371 shadowed_by: usize,
372 },
373
374 /// A composition schema declares no branches.
375 ///
376 /// Raised for empty `anyOf` and, today, for empty component `allOf` that is routed
377 /// through the shared empty composition-shape check.
378 ///
379 /// Error message: `{context} must declare at least one `anyOf` branch`
380 #[error("{context} must declare at least one `anyOf` branch")]
381 EmptyAnyOf { context: String },
382
383 /// `anyOf` component schemas form a recursive union cycle.
384 ///
385 /// Error message: `{context} forms a recursive `anyOf` cycle through schema `{schema}``
386 #[error("{context} forms a recursive `anyOf` cycle through schema `{schema}`")]
387 RecursiveAnyOf { context: String, schema: String },
388
389 /// A discriminator union does not use exactly one non-empty `anyOf` or `oneOf` branch list.
390 ///
391 /// Error message: `{context} discriminator unions must declare exactly one non-empty `anyOf` or `oneOf` branch list`
392 #[error(
393 "{context} discriminator unions must declare exactly one non-empty `anyOf` or `oneOf` branch list"
394 )]
395 InvalidDiscriminatorUnion { context: String },
396
397 /// A discriminator union branch is not a local component schema reference.
398 ///
399 /// Error message: `{context}.{keyword}[{index}] must be a local component schema reference when using `discriminator``
400 #[error(
401 "{context}.{keyword}[{index}] must be a local component schema reference when using `discriminator`"
402 )]
403 UnsupportedDiscriminatorBranch {
404 context: String,
405 keyword: &'static str,
406 index: usize,
407 },
408
409 /// A discriminator union branch target does not generate as an object struct.
410 ///
411 /// Error message: `{context} discriminator branch `{schema}` must be an object struct component`
412 #[error("{context} discriminator branch `{schema}` must be an object struct component")]
413 DiscriminatorBranchNotObject { context: String, schema: String },
414
415 /// A discriminator branch object contains the discriminator property.
416 ///
417 /// Error message: `{context} discriminator branch `{schema}` contains discriminator property `{property}``
418 #[error(
419 "{context} discriminator branch `{schema}` contains discriminator property `{property}`"
420 )]
421 DiscriminatorPropertyConflict {
422 context: String,
423 schema: String,
424 property: String,
425 },
426
427 /// A discriminator branch object contains an invalid embedded discriminator property.
428 ///
429 /// Error message: `{context} discriminator branch `{schema}` property `{property}` must be {expected}`
430 #[error("{context} discriminator branch `{schema}` property `{property}` must be {expected}")]
431 InvalidDiscriminatorProperty {
432 context: String,
433 schema: String,
434 property: String,
435 expected: &'static str,
436 },
437
438 /// A discriminator mapping entry targets a non-local schema or a schema outside the union branches.
439 ///
440 /// Error message: `{context}.discriminator.mapping[{value:?}] targets `{target}`, which is not a local union branch schema`
441 #[error(
442 "{context}.discriminator.mapping[{value:?}] targets `{target}`, which is not a local union branch schema"
443 )]
444 InvalidDiscriminatorMapping {
445 context: String,
446 value: String,
447 target: String,
448 },
449
450 /// Multiple discriminator mapping values target the same union branch schema.
451 ///
452 /// Error message: `{context}.discriminator.mapping maps multiple values to branch schema `{schema}``
453 #[error("{context}.discriminator.mapping maps multiple values to branch schema `{schema}`")]
454 DuplicateDiscriminatorMapping { context: String, schema: String },
455
456 /// A discriminator mapping value disagrees with a branch's embedded discriminator property value.
457 ///
458 /// Error message: `{context}.discriminator.mapping maps value `{value}` to branch schema `{schema}`, but the branch declares discriminator value `{actual}`
459 #[error(
460 "{context}.discriminator.mapping maps value `{value}` to branch schema `{schema}`, but the branch declares discriminator value `{actual}`"
461 )]
462 DiscriminatorMappingValueMismatch {
463 context: String,
464 schema: String,
465 value: String,
466 actual: String,
467 },
468
469 /// Multiple discriminator branches resolve to the same discriminator value after implicit defaults are applied.
470 ///
471 /// Error message: `{context}.discriminator resolves multiple branch schemas to value `{value}``
472 #[error("{context}.discriminator resolves multiple branch schemas to value `{value}`")]
473 DuplicateDiscriminatorValue { context: String, value: String },
474
475 // -- Schema constraint validation --
476 /// A string schema specifies a `minLength` greater than its `maxLength`.
477 ///
478 /// Error message: `{context} has minLength {min_length} greater than maxLength {max_length}`
479 #[error("{context} has minLength {min_length} greater than maxLength {max_length}")]
480 InvalidStringLengthBounds {
481 context: String,
482 min_length: u64,
483 max_length: u64,
484 },
485
486 /// A schema uses `uniqueItems`, which cannot be enforced by generated `Vec`-backed types.
487 ///
488 /// Error message: `{context} uses `uniqueItems`; generated Vec-backed types cannot enforce uniqueness yet`
489 #[error(
490 "{context} uses `uniqueItems`; generated Vec-backed types cannot enforce uniqueness yet"
491 )]
492 UniqueItemsUnsupported { context: String },
493
494 /// An array schema specifies `minItems` greater than `maxItems`.
495 ///
496 /// Error message: `{context} has minItems {min_items} greater than maxItems {max_items}`
497 #[error("{context} has minItems {min_items} greater than maxItems {max_items}")]
498 InvalidArrayLengthBounds {
499 context: String,
500 min_items: u64,
501 max_items: u64,
502 },
503
504 /// A schema uses a keyword that is not safely supported.
505 ///
506 /// Error message: `{context} uses `{keyword}`, which is not safely supported yet`
507 #[error("{context} uses `{keyword}`, which is not safely supported yet")]
508 UnsupportedKeyword {
509 context: String,
510 keyword: &'static str,
511 },
512
513 /// A schema keyword that must be a non-negative integer has an invalid value.
514 ///
515 /// Error message: `{context}.{keyword} must be a non-negative integer`
516 #[error("{context}.{keyword} must be a non-negative integer")]
517 InvalidNonNegativeIntegerKeyword {
518 context: String,
519 keyword: &'static str,
520 },
521
522 /// A schema keyword that must be a boolean has an invalid value.
523 ///
524 /// Error message: `{context}.{keyword} must be a boolean`
525 #[error("{context}.{keyword} must be a boolean")]
526 InvalidBooleanKeyword {
527 context: String,
528 keyword: &'static str,
529 },
530
531 /// An `exclusiveMinimum`/`exclusiveMaximum` keyword is present but the corresponding bound is missing.
532 ///
533 /// Error message: `{context}.{exclusive_keyword} requires `{keyword}``
534 #[error("{context}.{exclusive_keyword} requires `{keyword}`")]
535 ExclusiveLimitRequiresBound {
536 context: String,
537 exclusive_keyword: &'static str,
538 keyword: &'static str,
539 },
540
541 /// A schema keyword that must be a finite number has a non-finite value.
542 ///
543 /// Error message: `{context}.{keyword} must be a finite number`
544 #[error("{context}.{keyword} must be a finite number")]
545 InvalidFiniteNumberKeyword {
546 context: String,
547 keyword: &'static str,
548 },
549
550 /// A value expected to be an integer is not.
551 ///
552 /// Error message: `{context} must be an integer`
553 #[error("{context} must be an integer")]
554 ExpectedInteger { context: String },
555
556 /// Integer bounds (minimum/maximum) do not permit any value.
557 ///
558 /// Error message: `{context} integer bounds do not allow any value`
559 #[error("{context} integer bounds do not allow any value")]
560 EmptyIntegerBounds { context: String },
561
562 /// An exclusive integer minimum overflows `i64`.
563 ///
564 /// Error message: `exclusive integer minimum overflows`
565 #[error("exclusive integer minimum overflows")]
566 ExclusiveIntegerMinimumOverflow,
567
568 /// An exclusive integer maximum overflows `i64`.
569 ///
570 /// Error message: `exclusive integer maximum overflows`
571 #[error("exclusive integer maximum overflows")]
572 ExclusiveIntegerMaximumOverflow,
573
574 /// Number bounds (minimum/maximum) do not permit any value.
575 ///
576 /// Error message: `{context} number bounds do not allow any value`
577 #[error("{context} number bounds do not allow any value")]
578 EmptyNumberBounds { context: String },
579
580 // -- Operation, parameter, and response validation --
581 /// An operation does not declare any responses.
582 ///
583 /// Error message: `operation `{operation_id}` must declare responses`
584 #[error("operation `{operation_id}` must declare responses")]
585 MissingOperationResponses { operation_id: String },
586
587 /// A value expected to be an array is not.
588 ///
589 /// Error message: `{context} must be an array`
590 #[error("{context} must be an array")]
591 ExpectedArray { context: String },
592
593 /// A parameter uses an unsupported location (e.g. cookie) instead of path, query, or header.
594 ///
595 /// Error message: `{context} parameter `{wire_name}` is in `{location}`; only path, query, and header parameters are supported`
596 #[error(
597 "{context} parameter `{wire_name}` is in `{location}`; only path, query, and header parameters are supported"
598 )]
599 UnsupportedParameterLocation {
600 context: String,
601 wire_name: String,
602 location: String,
603 },
604
605 /// A parameter uses `content` instead of `schema`.
606 ///
607 /// Error message: `{context} parameter `{wire_name}` uses `content`; schema parameters are required`
608 #[error("{context} parameter `{wire_name}` uses `content`; schema parameters are required")]
609 ContentParameterUnsupported { context: String, wire_name: String },
610
611 /// A parameter is missing a required `schema` declaration.
612 ///
613 /// Error message: `{context} parameter `{wire_name}` must declare schema`
614 #[error("{context} parameter `{wire_name}` must declare schema")]
615 MissingParameterSchema { context: String, wire_name: String },
616
617 /// A parameter is nullable, which is not supported.
618 ///
619 /// Error message: `parameter `{wire_name}` is nullable; nullable parameters are not supported`
620 #[error("parameter `{wire_name}` is nullable; nullable parameters are not supported")]
621 NullableParameterUnsupported { wire_name: String },
622
623 /// A parameter uses `anyOf`, which is not supported for URI/header encoding yet.
624 ///
625 /// Error message: `parameter `{wire_name}` uses `anyOf`; anyOf parameters are not supported yet`
626 #[error("parameter `{wire_name}` uses `anyOf`; anyOf parameters are not supported yet")]
627 AnyOfParameterUnsupported { wire_name: String },
628
629 /// A parameter is a map or arbitrary JSON value, which has no URI/header encoding.
630 ///
631 /// Error message: `parameter `{wire_name}` is a map or JSON value; map parameters are not supported`
632 #[error("parameter `{wire_name}` is a map or JSON value; map parameters are not supported")]
633 MapParameterUnsupported { wire_name: String },
634
635 /// A path parameter is an array, which is not supported.
636 ///
637 /// Error message: `path parameter `{wire_name}` is an array; array path parameter styles are not supported`
638 #[error(
639 "path parameter `{wire_name}` is an array; array path parameter styles are not supported"
640 )]
641 ArrayPathParameterUnsupported { wire_name: String },
642
643 /// A header parameter is an array, which is not supported.
644 ///
645 /// Error message: `header parameter `{wire_name}` is an array; array header parameter styles are not supported`
646 #[error(
647 "header parameter `{wire_name}` is an array; array header parameter styles are not supported"
648 )]
649 ArrayHeaderParameterUnsupported { wire_name: String },
650
651 /// A path parameter does not set `required: true`.
652 ///
653 /// Error message: `path parameter `{wire_name}` must set required: true`
654 #[error("path parameter `{wire_name}` must set required: true")]
655 PathParameterNotRequired { wire_name: String },
656
657 /// A context is missing a required `content` declaration.
658 ///
659 /// Error message: `{context} must declare content`
660 #[error("{context} must declare content")]
661 MissingContent { context: String },
662
663 /// A context is missing the required `application/json` content type.
664 ///
665 /// Error message: `{context} must declare application/json content`
666 #[error("{context} must declare application/json content")]
667 MissingJsonContent { context: String },
668
669 /// A context's `application/json` content is missing a schema.
670 ///
671 /// Error message: `{context} application/json content must declare schema`
672 #[error("{context} application/json content must declare schema")]
673 MissingJsonSchema { context: String },
674
675 /// A response body uses the `default` status, which is not yet supported for decoding.
676 ///
677 /// Error message: `{context} contains a default response body; default response decoding is not supported yet`
678 #[error(
679 "{context} contains a default response body; default response decoding is not supported yet"
680 )]
681 DefaultResponseBodyUnsupported { context: String },
682
683 /// A response contains an invalid HTTP status code string.
684 ///
685 /// Error message: `{context} contains invalid status code `{status}``
686 #[error("{context} contains invalid status code `{status}`")]
687 InvalidStatusCode { context: String, status: String },
688
689 /// A response contains a status code outside the valid 100–599 range.
690 ///
691 /// Error message: `{context} contains out-of-range status code `{status_code}``
692 #[error("{context} contains out-of-range status code `{status_code}`")]
693 OutOfRangeStatusCode { context: String, status_code: u16 },
694
695 /// A response for a given status code is missing `application/json` content.
696 ///
697 /// Error message: `{context} {status} response must declare application/json content`
698 #[error("{context} {status} response must declare application/json content")]
699 MissingResponseJsonContent { context: String, status: String },
700
701 /// A path template contains a parameter that is never closed.
702 ///
703 /// Error message: `path `{path}` contains an unclosed parameter`
704 #[error("path `{path}` contains an unclosed parameter")]
705 UnclosedPathParameter { path: String },
706
707 /// A path template contains an empty parameter (e.g. `{}`).
708 ///
709 /// Error message: `path `{path}` contains an empty parameter`
710 #[error("path `{path}` contains an empty parameter")]
711 EmptyPathParameter { path: String },
712
713 /// A path template references a parameter that is not declared in the operation's parameters.
714 ///
715 /// Error message: `path `{path}` uses parameter `{name}` but it is not declared`
716 #[error("path `{path}` uses parameter `{name}` but it is not declared")]
717 UndeclaredPathParameter { path: String, name: String },
718
719 /// A parameter is declared for a path but never used in the path template.
720 ///
721 /// Error message: `path parameter `{name}` is declared but not used in path `{path}``
722 #[error("path parameter `{name}` is declared but not used in path `{path}`")]
723 UnusedPathParameter { path: String, name: String },
724
725 // -- Reference resolution and JSON shape validation --
726 /// A `$ref` could not be resolved because the referenced component failed validation.
727 ///
728 /// Error message: `failed to resolve reference `{reference}` in {context}: {source}`
729 #[error("failed to resolve reference `{reference}` in {context}: {source}")]
730 ResolveReference {
731 reference: String,
732 context: String,
733 #[source]
734 source: Box<ValidationError>,
735 },
736
737 /// A reference points to an external document; only local (`#`) references are supported.
738 ///
739 /// Error message: `only local references are supported`
740 #[error("only local references are supported")]
741 NonLocalReference,
742
743 /// A local reference is not a valid JSON pointer.
744 ///
745 /// Error message: `local reference must be a JSON pointer`
746 #[error("local reference must be a JSON pointer")]
747 InvalidLocalReference,
748
749 /// A JSON pointer is missing a required token segment.
750 ///
751 /// Error message: `missing `{token}``
752 #[error("missing `{token}`")]
753 MissingJsonPointerToken { token: String },
754
755 /// A `$ref` does not point to the expected `#/components/{section}/…` path.
756 ///
757 /// Error message: `reference `{reference}` must point to #/components/{section}/...`
758 #[error("reference `{reference}` must point to #/components/{section}/...")]
759 InvalidComponentReference {
760 reference: String,
761 section: &'static str,
762 },
763
764 /// A local `$ref` chain references itself.
765 ///
766 /// Error message: `circular reference `{reference}``
767 #[error("circular reference `{reference}`")]
768 CircularReference { reference: String },
769
770 /// A value expected to be an object is not.
771 ///
772 /// Error message: `{context} must be an object`
773 #[error("{context} must be an object")]
774 ExpectedObject { context: String },
775
776 /// A nested field expected to be an object is not.
777 ///
778 /// Error message: `{context}.{field} must be an object`
779 #[error("{context}.{field} must be an object")]
780 ExpectedObjectField {
781 context: String,
782 field: &'static str,
783 },
784}