openapiv3/
paths.rs

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