Skip to main content

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 {}