Skip to main content

salvo_oapi/openapi/
callback.rs

1//! Implements [OpenAPI Callback Object][callback] for operations.
2//!
3//! [callback]: https://spec.openapis.org/oas/latest.html#callback-object
4use std::ops::{Deref, DerefMut};
5
6use serde::{Deserialize, Serialize};
7
8use super::PathItem;
9use crate::PropMap;
10
11/// Implements [OpenAPI Callback Object][callback].
12///
13/// A map of possible out-of-band callbacks related to the parent operation. Each value in
14/// the map is a [`PathItem`] that describes a set of requests that may be initiated by the API
15/// provider and the expected responses. The key value used to identify the [`PathItem`] is an
16/// expression, evaluated at runtime, that identifies a URL to use for the callback operation.
17///
18/// See the OpenAPI spec for the [Callback Object][callback] for more details on the runtime
19/// [expression][expression] syntax used as keys.
20///
21/// [callback]: https://spec.openapis.org/oas/latest.html#callback-object
22/// [expression]: https://spec.openapis.org/oas/latest.html#runtime-expressions
23#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
24pub struct Callback(pub PropMap<String, PathItem>);
25
26impl Callback {
27    /// Construct a new empty [`Callback`].
28    #[must_use]
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Returns `true` if the callback contains no entries.
34    #[must_use]
35    pub fn is_empty(&self) -> bool {
36        self.0.is_empty()
37    }
38
39    /// Insert a runtime expression to [`PathItem`] mapping and return `self`.
40    #[must_use]
41    pub fn path<S: Into<String>, P: Into<PathItem>>(mut self, expression: S, path_item: P) -> Self {
42        self.0.insert(expression.into(), path_item.into());
43        self
44    }
45
46    /// Insert a runtime expression to [`PathItem`] mapping into the callback.
47    pub fn insert<S: Into<String>, P: Into<PathItem>>(&mut self, expression: S, path_item: P) {
48        self.0.insert(expression.into(), path_item.into());
49    }
50}
51
52impl Deref for Callback {
53    type Target = PropMap<String, PathItem>;
54
55    fn deref(&self) -> &Self::Target {
56        &self.0
57    }
58}
59
60impl DerefMut for Callback {
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.0
63    }
64}
65
66impl IntoIterator for Callback {
67    type Item = (String, PathItem);
68    type IntoIter = <PropMap<String, PathItem> as IntoIterator>::IntoIter;
69
70    fn into_iter(self) -> Self::IntoIter {
71        self.0.into_iter()
72    }
73}
74
75impl<S, P> FromIterator<(S, P)> for Callback
76where
77    S: Into<String>,
78    P: Into<PathItem>,
79{
80    fn from_iter<I: IntoIterator<Item = (S, P)>>(iter: I) -> Self {
81        Self(
82            iter.into_iter()
83                .map(|(k, v)| (k.into(), v.into()))
84                .collect(),
85        )
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use assert_json_diff::assert_json_eq;
92    use serde_json::json;
93
94    use super::*;
95    use crate::{Operation, PathItemType};
96
97    #[test]
98    fn callback_default_is_empty() {
99        let callback = Callback::default();
100        assert!(callback.is_empty());
101    }
102
103    #[test]
104    fn callback_serializes_as_map_of_path_items() {
105        let callback = Callback::new().path(
106            "{$request.body#/callbackUrl}",
107            PathItem::new(PathItemType::Post, Operation::new()),
108        );
109
110        assert_json_eq!(
111            callback,
112            json!({
113                "{$request.body#/callbackUrl}": {
114                    "post": { "responses": {} }
115                }
116            })
117        );
118    }
119}