Skip to main content

salvo_oapi/openapi/
example.rs

1//! Implements [OpenAPI Example Object][example] can be used to define examples for
2//! [`Response`][response]s and [`RequestBody`][request_body]s.
3//!
4//! [example]: https://spec.openapis.org/oas/latest.html#example-object
5//! [response]: response/struct.Response.html
6//! [request_body]: request_body/struct.RequestBody.html
7use serde::{Deserialize, Serialize};
8
9/// Implements [OpenAPI Example Object][example].
10///
11/// Example is used on path operations to describe possible response bodies.
12///
13/// [example]: https://spec.openapis.org/oas/latest.html#example-object
14#[non_exhaustive]
15#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)]
16#[serde(rename_all = "camelCase")]
17pub struct Example {
18    /// Short description for the [`Example`].
19    #[serde(default, skip_serializing_if = "String::is_empty")]
20    pub summary: String,
21
22    /// Long description for the [`Example`]. Value supports markdown syntax for rich text
23    /// representation.
24    #[serde(default, skip_serializing_if = "String::is_empty")]
25    pub description: String,
26
27    /// Embedded literal example value. [`Example::value`] and [`Example::external_value`] are
28    /// mutually exclusive.
29    ///
30    /// Deprecated for non-JSON serialization targets in OpenAPI 3.2; prefer
31    /// [`Example::data_value`] and/or [`Example::serialized_value`].
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub value: Option<serde_json::Value>,
34
35    /// An example of the data structure, which must be valid against the relevant schema. Added
36    /// in OpenAPI 3.2. When present, [`Example::value`] must be absent.
37    ///
38    /// See <https://spec.openapis.org/oas/v3.2.0.html#example-object>.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub data_value: Option<serde_json::Value>,
41
42    /// An example of the serialized form of the value, including encoding and escaping. Added
43    /// in OpenAPI 3.2.
44    ///
45    /// When [`Example::data_value`] is present this should be the serialization of that data.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub serialized_value: Option<String>,
48
49    /// An URI that points to a literal example value. [`Example::external_value`] provides the
50    /// capability to references an example that cannot be easily included in JSON or YAML.
51    /// [`Example::value`] and [`Example::external_value`] are mutually exclusive.
52    #[serde(default, skip_serializing_if = "String::is_empty")]
53    pub external_value: String,
54}
55
56impl Example {
57    /// Construct a new empty [`Example`]. This is effectively same as calling [`Example::default`].
58    #[must_use]
59    pub fn new() -> Self {
60        Self::default()
61    }
62    /// Add or change a short description for the [`Example`]. Setting this to empty `String`
63    /// will make it not render in the generated OpenAPI document.
64    #[must_use]
65    pub fn summary<S: Into<String>>(mut self, summary: S) -> Self {
66        self.summary = summary.into();
67        self
68    }
69
70    /// Add or change a long description for the [`Example`]. Markdown syntax is supported for rich
71    /// text representation.
72    ///
73    /// Setting this to empty `String` will make it not render in the generated
74    /// OpenAPI document.
75    #[must_use]
76    pub fn description<D: Into<String>>(mut self, description: D) -> Self {
77        self.description = description.into();
78        self
79    }
80
81    /// Add or change embedded literal example value. [`Example::value`] and
82    /// [`Example::external_value`] are mutually exclusive.
83    #[must_use]
84    pub fn value(mut self, value: serde_json::Value) -> Self {
85        self.value = Some(value);
86        self
87    }
88
89    /// Add or change the structured example data. Requires OpenAPI 3.2.
90    ///
91    /// [`Example::data_value`] and [`Example::value`] are mutually exclusive.
92    #[must_use]
93    pub fn data_value(mut self, data_value: serde_json::Value) -> Self {
94        self.data_value = Some(data_value);
95        self
96    }
97
98    /// Add or change the serialized form of the example. Requires OpenAPI 3.2.
99    #[must_use]
100    pub fn serialized_value<S: Into<String>>(mut self, serialized_value: S) -> Self {
101        self.serialized_value = Some(serialized_value.into());
102        self
103    }
104
105    /// Add or change an URI that points to a literal example value. [`Example::external_value`]
106    /// provides the capability to references an example that cannot be easily included
107    /// in JSON or YAML. [`Example::value`] and [`Example::external_value`] are mutually exclusive.
108    ///
109    /// Setting this to an empty String will make the field not to render in the generated OpenAPI
110    /// document.
111    #[must_use]
112    pub fn external_value<E: Into<String>>(mut self, external_value: E) -> Self {
113        self.external_value = external_value.into();
114        self
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_example() {
124        let example = Example::new();
125        assert!(example.summary.is_empty());
126        assert!(example.description.is_empty());
127        assert!(example.value.is_none());
128        assert!(example.external_value.is_empty());
129
130        let example = example.summary("summary");
131        assert_eq!(example.summary, "summary");
132
133        let example = example.description("description");
134        assert_eq!(example.description, "description");
135
136        let example = example.external_value("external_value");
137        assert_eq!(example.external_value, "external_value");
138
139        let example = example.value(serde_json::Value::String("value".to_owned()));
140        assert!(example.value.is_some());
141        assert_eq!(
142            example.value.unwrap(),
143            serde_json::Value::String("value".to_owned())
144        );
145    }
146
147    #[test]
148    fn example_openapi_3_2_fields_round_trip() {
149        let example = Example::new()
150            .data_value(serde_json::json!({ "lat": 10, "long": 60 }))
151            .serialized_value(r#"{"lat":10,"long":60}"#);
152
153        let value = serde_json::to_value(&example).expect("serialize");
154        assert_eq!(
155            value,
156            serde_json::json!({
157                "dataValue": { "lat": 10, "long": 60 },
158                "serializedValue": r#"{"lat":10,"long":60}"#
159            })
160        );
161
162        let parsed: Example = serde_json::from_value(value).expect("deserialize");
163        assert_eq!(parsed, example);
164    }
165}