openapiv3/
paths.rs

1use std::marker::PhantomData;
2
3use crate::*;
4use indexmap::IndexMap;
5use http::Method;
6use serde::{Deserialize, Deserializer, Serialize};
7
8/// Describes the operations available on a single path.
9/// A Path Item MAY be empty, due to ACL constraints.
10/// The path itself is still exposed to the documentation
11/// viewer but they will not know which operations and
12/// parameters are available.
13#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
14pub struct PathItem {
15    /// An optional, string summary, intended to apply to all operations in
16    /// this path.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub summary: Option<String>,
19    /// An optional, string description, intended to apply to all operations in
20    /// this path. CommonMark syntax MAY be used for rich text representation.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub description: Option<String>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub get: Option<Operation>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub put: Option<Operation>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub post: Option<Operation>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub delete: Option<Operation>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub options: Option<Operation>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub head: Option<Operation>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub patch: Option<Operation>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub trace: Option<Operation>,
39    /// An alternative server array to service all operations in this path.
40    #[serde(default, skip_serializing_if = "Vec::is_empty")]
41    pub servers: Vec<Server>,
42    /// A list of parameters that are applicable for all the
43    /// operations described under this path. These parameters
44    /// can be overridden at the operation level, but cannot be
45    /// removed there. The list MUST NOT include duplicated parameters.
46    /// A unique parameter is defined by a combination of a name and location.
47    /// The list can use the Reference Object to link to parameters that
48    /// are defined at the OpenAPI Object's components/parameters.
49    #[serde(default)]
50    #[serde(skip_serializing_if = "Vec::is_empty")]
51    pub parameters: Vec<RefOr<Parameter>>,
52    /// Inline extensions to this object.
53    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
54    pub extensions: IndexMap<String, serde_json::Value>,
55}
56
57impl PathItem {
58    /// Returns an iterator of references to the [Operation]s in the [PathItem].
59    pub fn iter(&self) -> impl Iterator<Item=(&str, &'_ Operation)> {
60        vec![
61            ("get", &self.get),
62            ("put", &self.put),
63            ("post", &self.post),
64            ("delete", &self.delete),
65            ("options", &self.options),
66            ("head", &self.head),
67            ("patch", &self.patch),
68            ("trace", &self.trace),
69        ]
70            .into_iter()
71            .filter_map(|(method, maybe_op)| maybe_op.as_ref().map(|op| (method, op)))
72    }
73
74    pub fn iter_mut(&mut self) -> impl Iterator<Item=(&str, &'_ mut Operation)> {
75        vec![
76            ("get", &mut self.get),
77            ("put", &mut self.put),
78            ("post", &mut self.post),
79            ("delete", &mut self.delete),
80            ("options", &mut self.options),
81            ("head", &mut self.head),
82            ("patch", &mut self.patch),
83            ("trace", &mut self.trace),
84        ]
85            .into_iter()
86            .filter_map(|(method, maybe_op)| maybe_op.as_mut().map(|op| (method, op)))
87    }
88
89    pub fn get(operation: Operation) -> Self {
90        Self {
91            get: Some(operation),
92            ..PathItem::default()
93        }
94    }
95
96    pub fn post(operation: Operation) -> Self {
97        Self {
98            post: Some(operation),
99            ..PathItem::default()
100        }
101    }
102}
103
104impl IntoIterator for PathItem {
105    type Item = (&'static str, Operation);
106
107    type IntoIter = std::vec::IntoIter<Self::Item>;
108
109    /// Returns an iterator of the [Operation]s in the [PathItem].
110    fn into_iter(self) -> Self::IntoIter {
111        vec![
112            ("get", self.get),
113            ("put", self.put),
114            ("post", self.post),
115            ("delete", self.delete),
116            ("options", self.options),
117            ("head", self.head),
118            ("patch", self.patch),
119            ("trace", self.trace),
120        ]
121            .into_iter()
122            .filter_map(|(method, maybe_op)| maybe_op.map(|op| (method, op)))
123            .collect::<Vec<_>>()
124            .into_iter()
125    }
126}
127
128/// Holds the relative paths to the individual endpoints and
129/// their operations. The path is appended to the URL from the
130/// Server Object in order to construct the full URL. The Paths
131/// MAY be empty, due to ACL constraints.
132#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
133pub struct Paths {
134    /// A map of PathItems or references to them.
135    #[serde(flatten, deserialize_with = "deserialize_paths")]
136    pub paths: IndexMap<String, RefOr<PathItem>>,
137    /// Inline extensions to this object.
138    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
139    pub extensions: IndexMap<String, serde_json::Value>,
140}
141
142impl std::ops::Deref for Paths {
143    type Target = IndexMap<String, RefOr<PathItem>>;
144
145    fn deref(&self) -> &Self::Target {
146        &self.paths
147    }
148}
149
150impl std::ops::DerefMut for Paths {
151    fn deref_mut(&mut self) -> &mut Self::Target {
152        &mut self.paths
153    }
154}
155
156impl Paths {
157    pub fn insert(&mut self, key: String, path_item: PathItem) -> Option<RefOr<PathItem>> {
158        self.paths.insert(key, RefOr::Item(path_item))
159    }
160
161    pub fn insert_operation(&mut self, path: String, method: Method, operation: Operation) -> Option<Operation> {
162        let item = self.paths.entry(path).or_default();
163        let item = item.as_mut().expect("Currently don't support references for PathItem");
164        match method {
165            Method::GET => item.get.replace(operation),
166            Method::PUT => item.put.replace(operation),
167            Method::POST => item.post.replace(operation),
168            Method::DELETE => item.delete.replace(operation),
169            Method::PATCH => item.patch.replace(operation),
170            Method::HEAD => item.head.replace(operation),
171            Method::OPTIONS => item.options.replace(operation),
172            Method::TRACE => item.trace.replace(operation),
173            _ => panic!("Unsupported method: {:?}", method),
174        }
175    }
176}
177
178impl IntoIterator for Paths {
179    type Item = (String, RefOr<PathItem>);
180
181    type IntoIter = indexmap::map::IntoIter<String, RefOr<PathItem>>;
182
183    fn into_iter(self) -> Self::IntoIter {
184        self.paths.into_iter()
185    }
186}
187
188fn deserialize_paths<'de, D>(
189    deserializer: D,
190) -> Result<IndexMap<String, RefOr<PathItem>>, D::Error>
191    where
192        D: Deserializer<'de>,
193{
194    deserializer.deserialize_map(PredicateVisitor(
195        |key: &String| key.starts_with('/'),
196        PhantomData,
197    ))
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn test_path_item_iterators() {
206        let operation = Operation::default();
207
208        let path_item = PathItem {
209            get: Some(operation.clone()),
210            post: Some(operation.clone()),
211            delete: Some(operation.clone()),
212            ..Default::default()
213        };
214
215        let expected = vec![
216            ("get", &operation),
217            ("post", &operation),
218            ("delete", &operation),
219        ];
220        assert_eq!(path_item.iter().collect::<Vec<_>>(), expected);
221
222        let expected = vec![
223            ("get", operation.clone()),
224            ("post", operation.clone()),
225            ("delete", operation.clone()),
226        ];
227        assert_eq!(path_item.into_iter().collect::<Vec<_>>(), expected);
228    }
229}