Skip to main content

openapiv3_resolve/
component.rs

1use crate::Section;
2use indexmap::IndexMap;
3use openapiv3::{
4    Callback, Example, Header, Link, OpenAPI, Parameter, PathItem, ReferenceOr, RequestBody,
5    Response, Schema, SecurityScheme,
6};
7
8/// A type that `$ref` pointers can name.
9///
10/// Implemented for the nine `#/components` types and for [`PathItem`]. The
11/// type is what decides which section is searched, so a pointer into a
12/// different section is reported as
13/// [`ResolveError::SectionMismatch`](crate::ResolveError::SectionMismatch)
14/// rather than silently missing.
15///
16/// Note that [`Callback`] is a transparent alias for
17/// `IndexMap<String, PathItem>` rather than a distinct type, so *any* value of
18/// that shape resolves as a callback. That is upstream's design, not a choice
19/// this crate can undo.
20///
21/// This trait is sealed: it cannot be implemented outside this crate. That
22/// keeps [`Self::SECTION`] and [`Self::section`] in agreement, and it is what
23/// makes the blanket impls on `ReferenceOr<T>` and `ReferenceOr<Box<T>>`
24/// non-overlapping for good.
25pub trait Component: sealed::Sealed + Sized {
26    /// The section of the document holding values of this type.
27    const SECTION: Section;
28
29    /// Borrows that section, or `None` if the document omits it entirely.
30    fn section(openapi: &OpenAPI) -> Option<&IndexMap<String, ReferenceOr<Self>>>;
31}
32
33mod sealed {
34    pub trait Sealed {}
35}
36
37macro_rules! component {
38    ($ty:ty, $section:ident, $field:ident) => {
39        impl sealed::Sealed for $ty {}
40
41        impl Component for $ty {
42            const SECTION: Section = Section::$section;
43
44            fn section(openapi: &OpenAPI) -> Option<&IndexMap<String, ReferenceOr<Self>>> {
45                Some(&openapi.components.as_ref()?.$field)
46            }
47        }
48    };
49}
50
51// The section name is the wire spelling, which `Components` renames from its
52// Rust field name; deriving one from the other is what made `requestBodies`
53// and `securitySchemes` unresolvable.
54component!(Callback, Callbacks, callbacks);
55component!(Example, Examples, examples);
56component!(Header, Headers, headers);
57component!(Link, Links, links);
58component!(Parameter, Parameters, parameters);
59component!(RequestBody, RequestBodies, request_bodies);
60component!(Response, Responses, responses);
61component!(Schema, Schemas, schemas);
62component!(SecurityScheme, SecuritySchemes, security_schemes);
63
64impl sealed::Sealed for PathItem {}
65
66impl Component for PathItem {
67    const SECTION: Section = Section::Paths;
68
69    fn section(openapi: &OpenAPI) -> Option<&IndexMap<String, ReferenceOr<Self>>> {
70        Some(&openapi.paths.paths)
71    }
72}