okapi_operation/
components.rs

1use okapi::{
2    openapi3::{RefOr, SchemaObject, SecurityScheme},
3    schemars::{
4        JsonSchema,
5        gen::{SchemaGenerator, SchemaSettings},
6    },
7};
8
9/// Builder for [`Components`]
10pub struct ComponentsBuilder {
11    components: okapi::openapi3::Components,
12    inline_subschemas: bool,
13}
14
15#[allow(clippy::derivable_impls)]
16impl Default for ComponentsBuilder {
17    fn default() -> Self {
18        Self {
19            components: Default::default(),
20            inline_subschemas: false,
21        }
22    }
23}
24
25impl ComponentsBuilder {
26    pub fn okapi_components(mut self, components: okapi::openapi3::Components) -> Self {
27        self.components = components;
28        self
29    }
30
31    /// Enable or disable subschemas [inlining](https://docs.rs/schemars/latest/schemars/gen/struct.SchemaSettings.html#structfield.inline_subschemas).
32    ///
33    /// `false` by default.
34    pub fn inline_subschemas(mut self, inline_subschemas: bool) -> Self {
35        self.inline_subschemas = inline_subschemas;
36        self
37    }
38
39    pub fn build(self) -> Components {
40        let mut generator_settings = SchemaSettings::openapi3();
41        generator_settings.inline_subschemas = self.inline_subschemas;
42        Components {
43            generator: generator_settings.into_generator(),
44            components: self.components,
45        }
46    }
47}
48
49/// Storage for reusable components (schemas/parameters/responses/...).
50#[derive(Clone)]
51pub struct Components {
52    generator: SchemaGenerator,
53    components: okapi::openapi3::Components,
54}
55
56impl Components {
57    pub(crate) fn new(components: okapi::openapi3::Components) -> Self {
58        ComponentsBuilder::default()
59            .okapi_components(components)
60            .build()
61    }
62
63    /// Get schema for type.
64    pub fn schema_for<T: JsonSchema>(&mut self) -> SchemaObject {
65        let mut object = self.generator.subschema_for::<T>().into_object();
66        for visitor in self.generator.visitors_mut() {
67            visitor.visit_schema_object(&mut object);
68        }
69        object
70    }
71
72    /// Add security scheme to components.
73    pub fn add_security_scheme<N>(&mut self, name: N, sec: SecurityScheme)
74    where
75        N: Into<String>,
76    {
77        self.components
78            .security_schemes
79            .insert(name.into(), RefOr::Object(sec));
80    }
81
82    /// Generate [`okapi::openapi3::Components`].
83    pub(crate) fn okapi_components(
84        &mut self,
85    ) -> Result<okapi::openapi3::Components, anyhow::Error> {
86        let mut components = self.components.clone();
87        for (name, mut schema_object) in self
88            .generator
89            .definitions()
90            .iter()
91            .map(|(n, s)| (n.clone(), s.clone().into_object()))
92            .collect::<Vec<_>>()
93        {
94            for visitor in self.generator.visitors_mut() {
95                visitor.visit_schema_object(&mut schema_object);
96            }
97            if components.schemas.contains_key(&name) {
98                return Err(anyhow::anyhow!("Multiple schemas found for '{}'", name));
99            }
100            let _ = components.schemas.insert(name, schema_object);
101        }
102        Ok(components)
103    }
104}