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