openapiv3_resolve/error.rs
1use crate::Section;
2use std::fmt;
3
4/// Everything that can go wrong while resolving a `$ref`.
5///
6/// The variants distinguish a broken document (a dangling name, a `$ref` to
7/// the wrong kind of component) from a reference this crate structurally
8/// cannot follow (another file, a pointer into the middle of a component), so
9/// a caller can decide which ones are worth aborting on.
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum ResolveError {
13 /// The reference is not a JSON pointer into the current document.
14 NotALocalReference {
15 /// The reference as it appeared in the document.
16 reference: String,
17 },
18 /// The reference points into another document, which this crate does not load.
19 ExternalDocument {
20 /// The part of the reference before the `#`, e.g. `common.yaml`.
21 document: String,
22 },
23 /// The reference is a local pointer but has too few segments to name a component.
24 MalformedPointer {
25 /// The reference as it appeared in the document.
26 reference: String,
27 },
28 /// The pointer starts at a section of the document that cannot hold `$ref` targets.
29 UnsupportedRootSection {
30 /// The first pointer segment, e.g. `info`.
31 section: String,
32 },
33 /// The pointer names a `#/components` sub-section that does not exist.
34 UnknownSection {
35 /// The offending segment, e.g. `request_bodies` instead of `requestBodies`.
36 section: String,
37 },
38 /// The pointer names a component but the document has no `components` object.
39 ComponentsMissing {
40 /// The section the pointer asked for.
41 section: Section,
42 },
43 /// The pointer is well formed but names something the section does not contain.
44 NotFound {
45 /// The section that was searched.
46 section: Section,
47 /// The component name, after JSON pointer unescaping.
48 name: String,
49 },
50 /// The pointer names a valid component of a different kind than the caller asked for.
51 SectionMismatch {
52 /// The section implied by the requested type.
53 expected: Section,
54 /// The section the pointer actually names.
55 found: Section,
56 },
57 /// The pointer continues past a component, into its contents.
58 PointerTooDeep {
59 /// The reference as it appeared in the document.
60 reference: String,
61 },
62 /// The chain of references never reached an inline item; the document is
63 /// most likely cyclic.
64 ReferenceChainTooLong {
65 /// The reference the walk started from.
66 reference: String,
67 /// The reference the walk gave up on, which is where the cycle runs.
68 last: String,
69 /// The hop limit that was hit, [`crate::MAX_REFERENCE_HOPS`].
70 max_hops: usize,
71 },
72 /// A component other than a schema contains, directly or through other
73 /// components, a `$ref` back to itself, so it has no finite fully resolved
74 /// form. A header whose content encoding names that same header is the
75 /// one way a document can do this.
76 ///
77 /// Schemas are allowed to contain themselves; see
78 /// [`NestedSchema`](crate::NestedSchema). Only
79 /// [`ResolvedOpenAPI`](crate::ResolvedOpenAPI) reports this; borrowing
80 /// resolution stops at the first item and never notices the cycle.
81 CyclicReference {
82 /// The `$ref` that closed the cycle, as it appeared in the document.
83 reference: String,
84 },
85 /// A discriminator on a `oneOf` or `anyOf` schema maps a value to a
86 /// schema that is not one of the alternatives, so the mapping could never
87 /// select anything. Only
88 /// [`ResolvedOpenAPI`](crate::ResolvedOpenAPI) reports this.
89 DiscriminatorMappingMismatch {
90 /// The discriminator's `propertyName`.
91 property_name: String,
92 /// The payload value whose mapping is wrong.
93 value: String,
94 /// The name of the schema the mapping value denotes.
95 schema: String,
96 },
97}
98
99impl fmt::Display for ResolveError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 match self {
102 Self::NotALocalReference { reference } => {
103 write!(
104 f,
105 "`{reference}` is not a local JSON pointer (expected `#/...`)"
106 )
107 }
108 Self::ExternalDocument { document } => {
109 write!(
110 f,
111 "reference points into `{document}`, which is a separate document"
112 )
113 }
114 Self::MalformedPointer { reference } => {
115 write!(f, "`{reference}` does not name a component")
116 }
117 Self::UnsupportedRootSection { section } => {
118 write!(f, "cannot resolve references into `{section}`")
119 }
120 Self::UnknownSection { section } => {
121 write!(f, "`{section}` is not a known section of `#/components`")
122 }
123 Self::ComponentsMissing { section } => {
124 write!(
125 f,
126 "document has no `components` object to look up `{section}` in"
127 )
128 }
129 Self::NotFound { section, name } => {
130 write!(f, "`{section}` does not contain `{name}`")
131 }
132 Self::SectionMismatch { expected, found } => {
133 write!(
134 f,
135 "expected a reference into `{expected}` but got one into `{found}`"
136 )
137 }
138 Self::PointerTooDeep { reference } => {
139 write!(
140 f,
141 "`{reference}` points inside a component; only whole components resolve"
142 )
143 }
144 Self::ReferenceChainTooLong {
145 reference,
146 last,
147 max_hops,
148 } => {
149 write!(
150 f,
151 "`{reference}` did not reach an item within {max_hops} hops, \
152 still at `{last}` (cyclic?)"
153 )
154 }
155 Self::CyclicReference { reference } => {
156 write!(
157 f,
158 "`{reference}` refers back to a non-schema component that contains it, \
159 which cannot be fully resolved"
160 )
161 }
162 Self::DiscriminatorMappingMismatch {
163 property_name,
164 value,
165 schema,
166 } => {
167 write!(
168 f,
169 "discriminator `{property_name}` maps `{value}` to `{schema}`, \
170 which is not one of the schema's alternatives"
171 )
172 }
173 }
174 }
175}
176
177impl std::error::Error for ResolveError {}