Skip to main content

utoipa/openapi/
content.rs

1//! Implements content object for request body and response.
2use std::collections::BTreeMap;
3
4use serde::{Deserialize, Serialize};
5
6use serde_json::Value;
7
8use super::builder;
9use super::example::Example;
10use super::extensions::Extensions;
11use super::{encoding::Encoding, set_value, RefOr, Schema};
12
13builder! {
14    ContentBuilder;
15
16
17    /// Content holds request body content or response content.
18    ///
19    /// [`Content`] implements OpenAPI spec [Media Type Object][media_type]
20    ///
21    /// [media_type]: <https://spec.openapis.org/oas/latest.html#media-type-object>
22    #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
23    #[cfg_attr(feature = "debug", derive(Debug))]
24    #[non_exhaustive]
25    #[serde(rename_all = "camelCase")]
26    pub struct Content {
27        /// Schema used in response body or request body.
28        #[serde(skip_serializing_if = "Option::is_none")]
29        pub schema: Option<RefOr<Schema>>,
30
31        /// Schema used for each item in a streaming or event-based media type.
32        #[serde(skip_serializing_if = "Option::is_none")]
33        pub item_schema: Option<RefOr<Schema>>,
34
35        /// Description of this media type. Markdown syntax is supported.
36        #[serde(skip_serializing_if = "Option::is_none")]
37        pub description: Option<String>,
38
39        /// Example for request body or response body.
40        #[serde(skip_serializing_if = "Option::is_none")]
41        pub example: Option<Value>,
42
43        /// Examples of the request body or response body. [`Content::examples`] should match to
44        /// media type and specified schema if present. [`Content::examples`] and
45        /// [`Content::example`] are mutually exclusive. If both are defined `examples` will
46        /// override value in `example`.
47        #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
48        pub examples: BTreeMap<String, RefOr<Example>>,
49
50        /// A map between a property name and its encoding information.
51        ///
52        /// The key, being the property name, MUST exist in the [`Content::schema`] as a property, with
53        /// `schema` being a [`Schema::Object`] and this object containing the same property key in
54        /// [`Object::properties`](crate::openapi::schema::Object::properties).
55        ///
56        /// The encoding object SHALL only apply to `request_body` objects when the media type is
57        /// multipart or `application/x-www-form-urlencoded`.
58        #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
59        pub encoding: BTreeMap<String, Encoding>,
60
61        /// Encoding information for tuple-like sequential or array items.
62        #[serde(skip_serializing_if = "Vec::is_empty", default)]
63        pub prefix_encoding: Vec<Encoding>,
64
65        /// Encoding information for each item in a sequential or array media type.
66        #[serde(skip_serializing_if = "Option::is_none")]
67        pub item_encoding: Option<Box<Encoding>>,
68
69        /// Optional extensions "x-something".
70        #[serde(skip_serializing_if = "Option::is_none", flatten)]
71        pub extensions: Option<Extensions>,
72    }
73}
74
75impl Content {
76    /// Construct a new [`Content`] object for provided _`schema`_.
77    pub fn new<I: Into<RefOr<Schema>>>(schema: Option<I>) -> Self {
78        Self {
79            schema: schema.map(|schema| schema.into()),
80            ..Self::default()
81        }
82    }
83}
84
85impl ContentBuilder {
86    /// Add schema.
87    pub fn schema<I: Into<RefOr<Schema>>>(mut self, schema: Option<I>) -> Self {
88        set_value!(self schema schema.map(|schema| schema.into()))
89    }
90
91    /// Add item schema for a streaming or event-based media type.
92    pub fn item_schema<I: Into<RefOr<Schema>>>(mut self, item_schema: Option<I>) -> Self {
93        set_value!(self item_schema item_schema.map(|schema| schema.into()))
94    }
95
96    /// Add or change description of this media type.
97    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
98        set_value!(self description description.map(Into::into))
99    }
100
101    /// Add example of schema.
102    pub fn example(mut self, example: Option<Value>) -> Self {
103        set_value!(self example example)
104    }
105
106    /// Add iterator of _`(N, V)`_ where `N` is name of example and `V` is [`Example`][example] to
107    /// [`Content`] of a request body or response body.
108    ///
109    /// [`Content::examples`] and [`Content::example`] are mutually exclusive. If both are defined
110    /// `examples` will override value in `example`.
111    ///
112    /// [example]: ../example/Example.html
113    pub fn examples_from_iter<
114        E: IntoIterator<Item = (N, V)>,
115        N: Into<String>,
116        V: Into<RefOr<Example>>,
117    >(
118        mut self,
119        examples: E,
120    ) -> Self {
121        self.examples.extend(
122            examples
123                .into_iter()
124                .map(|(name, example)| (name.into(), example.into())),
125        );
126
127        self
128    }
129
130    /// Add an encoding.
131    ///
132    /// The `property_name` MUST exist in the [`Content::schema`] as a property,
133    /// with `schema` being a [`Schema::Object`] and this object containing the same property
134    /// key in [`Object::properties`](crate::openapi::schema::Object::properties).
135    ///
136    /// The encoding object SHALL only apply to `request_body` objects when the media type is
137    /// multipart or `application/x-www-form-urlencoded`.
138    pub fn encoding<S: Into<String>, E: Into<Encoding>>(
139        mut self,
140        property_name: S,
141        encoding: E,
142    ) -> Self {
143        self.encoding.insert(property_name.into(), encoding.into());
144        self
145    }
146
147    /// Add encoding information for tuple-like sequential or array items.
148    pub fn prefix_encoding<I: IntoIterator<Item = E>, E: Into<Encoding>>(
149        mut self,
150        prefix_encoding: I,
151    ) -> Self {
152        set_value!(self prefix_encoding prefix_encoding.into_iter().map(Into::into).collect())
153    }
154
155    /// Add encoding information for each item in a sequential or array media type.
156    pub fn item_encoding<E: Into<Encoding>>(mut self, item_encoding: Option<E>) -> Self {
157        set_value!(self item_encoding item_encoding.map(|encoding| Box::new(encoding.into())))
158    }
159
160    /// Add openapi extensions (x-something) of the API.
161    pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
162        set_value!(self extensions extensions)
163    }
164}
165
166impl From<ContentBuilder> for RefOr<Content> {
167    fn from(content_builder: ContentBuilder) -> Self {
168        Self::T(content_builder.build())
169    }
170}
171
172impl From<super::Ref> for RefOr<Content> {
173    fn from(r: super::Ref) -> Self {
174        Self::Ref(r)
175    }
176}