Skip to main content

kynos_openapi/model/paths/
item.rs

1//! The Path Item Object.
2
3use serde::{Deserialize, Serialize};
4
5use crate::model::{
6    extensions::Extensions,
7    parameter::Parameter,
8    paths::{method::Method, operation::Operation},
9    reference::RefOr,
10    server::Server,
11};
12
13// `Map` backs `additional_operations`, which OpenAPI 3.2 introduced, so the
14// import is gated the same way the field is.
15#[cfg(feature = "openapi32")]
16use crate::Map;
17
18/// The operations available on a single path.
19///
20/// The per-method slots are boxed. An [`Operation`] is over a kilobyte, and
21/// inlining nine of them made this type 8.7 KB — a cost every
22/// [`Paths`](crate::model::paths::Paths) entry paid on insert and on rehash.
23/// Use [`operation`](PathItem::operation) and
24/// [`set_operation`](PathItem::set_operation) rather than touching the fields,
25/// and the indirection stays invisible.
26#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
27pub struct PathItem {
28    /// A reference to an external Path Item.
29    ///
30    /// The specification leaves the behaviour of fields adjacent to this
31    /// undefined, so Kynos never emits one.
32    #[serde(rename = "$ref", default, skip_serializing_if = "Option::is_none")]
33    pub reference: Option<String>,
34
35    /// A summary applying to every operation on this path.
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub summary: Option<String>,
38
39    /// A description applying to every operation on this path.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub description: Option<String>,
42
43    /// The `GET` operation.
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub get: Option<Box<Operation>>,
46
47    /// The `PUT` operation.
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub put: Option<Box<Operation>>,
50
51    /// The `POST` operation.
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub post: Option<Box<Operation>>,
54
55    /// The `DELETE` operation.
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub delete: Option<Box<Operation>>,
58
59    /// The `OPTIONS` operation.
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub options: Option<Box<Operation>>,
62
63    /// The `HEAD` operation.
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub head: Option<Box<Operation>>,
66
67    /// The `PATCH` operation.
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub patch: Option<Box<Operation>>,
70
71    /// The `TRACE` operation.
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub trace: Option<Box<Operation>>,
74
75    /// The `QUERY` operation.
76    ///
77    /// Introduced in OpenAPI 3.2.
78    #[cfg(feature = "openapi32")]
79    #[serde(default, skip_serializing_if = "Option::is_none")]
80    pub query: Option<Box<Operation>>,
81
82    /// Operations for methods with no dedicated field.
83    ///
84    /// Introduced in OpenAPI 3.2. Keys are HTTP methods with the exact
85    /// capitalization sent on the wire, and must not duplicate a method that
86    /// has its own field.
87    #[cfg(feature = "openapi32")]
88    #[serde(
89        rename = "additionalOperations",
90        default,
91        skip_serializing_if = "Map::is_empty"
92    )]
93    pub additional_operations: Map<Box<Operation>>,
94
95    /// Servers serving this path, overriding the document-level list.
96    #[serde(default, skip_serializing_if = "Vec::is_empty")]
97    pub servers: Vec<Server>,
98
99    /// Parameters applying to every operation on this path.
100    ///
101    /// Hoisting shared parameters here rather than repeating them on each
102    /// operation is what keeps a large description readable.
103    #[serde(default, skip_serializing_if = "Vec::is_empty")]
104    pub parameters: Vec<RefOr<Parameter>>,
105
106    /// Specification extensions.
107    #[serde(flatten)]
108    pub extensions: Extensions,
109}
110
111impl PathItem {
112    /// Creates a path item with no operations.
113    #[must_use]
114    pub fn new() -> Self {
115        Self::default()
116    }
117
118    /// Returns the operation for a method, if declared.
119    #[must_use]
120    pub fn operation(&self, method: Method) -> Option<&Operation> {
121        match method {
122            Method::Get => self.get.as_deref(),
123            Method::Put => self.put.as_deref(),
124            Method::Post => self.post.as_deref(),
125            Method::Delete => self.delete.as_deref(),
126            Method::Options => self.options.as_deref(),
127            Method::Head => self.head.as_deref(),
128            Method::Patch => self.patch.as_deref(),
129            Method::Trace => self.trace.as_deref(),
130            #[cfg(feature = "openapi32")]
131            Method::Query => self.query.as_deref(),
132        }
133    }
134
135    /// Sets the operation for a method, returning any operation it replaced.
136    pub fn set_operation(&mut self, method: Method, operation: Operation) -> Option<Operation> {
137        let slot = match method {
138            Method::Get => &mut self.get,
139            Method::Put => &mut self.put,
140            Method::Post => &mut self.post,
141            Method::Delete => &mut self.delete,
142            Method::Options => &mut self.options,
143            Method::Head => &mut self.head,
144            Method::Patch => &mut self.patch,
145            Method::Trace => &mut self.trace,
146            #[cfg(feature = "openapi32")]
147            Method::Query => &mut self.query,
148        };
149        slot.replace(Box::new(operation)).map(|boxed| *boxed)
150    }
151
152    /// Iterates over the declared operations and their methods.
153    pub fn operations(&self) -> impl Iterator<Item = (Method, &Operation)> {
154        Method::all()
155            .iter()
156            .filter_map(move |&method| self.operation(method).map(|op| (method, op)))
157    }
158
159    /// Sets the operation for a method, in builder style.
160    #[must_use]
161    pub fn with_operation(mut self, method: Method, operation: Operation) -> Self {
162        self.set_operation(method, operation);
163        self
164    }
165}