Skip to main content

openapi_nexus_core/traits/
openapi_ref_ext.rs

1use openapi_nexus_spec::oas31::spec::{Components, ObjectOrReference, ObjectSchema, Response};
2
3pub const COMPONENTS_PREFIX: &str = "#/components/";
4
5/// Extension trait for `ObjectOrReference::Ref` providing helpers to extract component names.
6pub trait OpenApiRefExt {
7    /// Returns the referenced schema name if the reference points to `#/components/schemas/...`.
8    fn schema_name(&self) -> Option<&str> {
9        self.component_name("schemas")
10    }
11
12    /// Returns the referenced response name if the reference points to `#/components/responses/...`.
13    fn response_name(&self) -> Option<&str> {
14        self.component_name("responses")
15    }
16
17    /// Returns the referenced parameter name if the reference points to `#/components/parameters/...`.
18    fn parameter_name(&self) -> Option<&str> {
19        self.component_name("parameters")
20    }
21
22    /// Returns the referenced example name if the reference points to `#/components/examples/...`.
23    fn example_name(&self) -> Option<&str> {
24        self.component_name("examples")
25    }
26
27    /// Returns the referenced request body name if the reference points to `#/components/requestBodies/...`.
28    fn request_body_name(&self) -> Option<&str> {
29        self.component_name("requestBodies")
30    }
31
32    /// Returns the referenced header name if the reference points to `#/components/headers/...`.
33    fn header_name(&self) -> Option<&str> {
34        self.component_name("headers")
35    }
36
37    /// Returns the referenced security scheme name if the reference points to `#/components/securitySchemes/...`.
38    fn security_scheme_name(&self) -> Option<&str> {
39        self.component_name("securitySchemes")
40    }
41
42    /// Returns the referenced link name if the reference points to `#/components/links/...`.
43    fn link_name(&self) -> Option<&str> {
44        self.component_name("links")
45    }
46
47    /// Returns the referenced callback name if the reference points to `#/components/callbacks/...`.
48    fn callback_name(&self) -> Option<&str> {
49        self.component_name("callbacks")
50    }
51
52    /// Returns the referenced path item name if the reference points to `#/components/pathItems/...`.
53    fn path_item_name(&self) -> Option<&str> {
54        self.component_name("pathItems")
55    }
56
57    /// Returns the component name for the given component type if the reference points inside `#/components/{component}/...`.
58    fn component_name(&self, component: &str) -> Option<&str>;
59
60    /// Resolve the referenced response from the supplied OpenAPI components.
61    fn resolve_response<'a>(&self, components: Option<&'a Components>) -> Option<&'a Response>;
62}
63
64impl OpenApiRefExt for ObjectOrReference<Response> {
65    fn component_name(&self, component: &str) -> Option<&str> {
66        match self {
67            ObjectOrReference::Ref { ref_path, .. } => extract_component_name(ref_path, component),
68            ObjectOrReference::Object(_) => None,
69        }
70    }
71
72    fn resolve_response<'a>(&self, components: Option<&'a Components>) -> Option<&'a Response> {
73        let components = components?;
74        let response_name = self.response_name()?;
75        match components.responses.get(response_name)? {
76            ObjectOrReference::Object(response) => Some(response),
77            ObjectOrReference::Ref { .. } => {
78                // TODO: Handle nested references
79                None
80            }
81        }
82    }
83}
84
85impl OpenApiRefExt for ObjectOrReference<ObjectSchema> {
86    fn component_name(&self, component: &str) -> Option<&str> {
87        match self {
88            ObjectOrReference::Ref { ref_path, .. } => extract_component_name(ref_path, component),
89            ObjectOrReference::Object(_) => None,
90        }
91    }
92
93    fn resolve_response<'a>(&self, _components: Option<&'a Components>) -> Option<&'a Response> {
94        None
95    }
96}
97
98fn extract_component_name<'a>(reference: &'a str, component: &str) -> Option<&'a str> {
99    let remainder = reference.strip_prefix(COMPONENTS_PREFIX)?;
100    let remainder = remainder.strip_prefix(component)?;
101    remainder.strip_prefix('/')
102}