openapi_nexus_core/traits/
openapi_ref_ext.rs

1use utoipa::openapi;
2
3const COMPONENTS_PREFIX: &str = "#/components/";
4
5/// Extension trait for `openapi::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>(
62        &self,
63        components: Option<&'a openapi::Components>,
64    ) -> Option<&'a openapi::Response>;
65}
66
67impl OpenApiRefExt for openapi::Ref {
68    fn component_name(&self, component: &str) -> Option<&str> {
69        extract_component_name(&self.ref_location, component)
70    }
71
72    fn resolve_response<'a>(
73        &self,
74        components: Option<&'a openapi::Components>,
75    ) -> Option<&'a openapi::Response> {
76        let components = components?;
77        let response_name = self.response_name()?;
78        match components.responses.get(response_name)? {
79            openapi::RefOr::T(response) => Some(response),
80            openapi::RefOr::Ref(inner_reference) => {
81                inner_reference.resolve_response(Some(components))
82            }
83        }
84    }
85}
86
87fn extract_component_name<'a>(reference: &'a str, component: &str) -> Option<&'a str> {
88    let remainder = reference.strip_prefix(COMPONENTS_PREFIX)?;
89    let remainder = remainder.strip_prefix(component)?;
90    remainder.strip_prefix('/')
91}