utoipa/openapi/
request_body.rs1use std::collections::BTreeMap;
5
6use serde::{Deserialize, Serialize};
7
8use super::extensions::Extensions;
9use super::{builder, set_value, Content, RefOr, Required};
10
11builder! {
12 RequestBodyBuilder;
13
14 #[non_exhaustive]
18 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
19 #[cfg_attr(feature = "debug", derive(Debug))]
20 #[serde(rename_all = "camelCase")]
21 pub struct RequestBody {
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub description: Option<String>,
25
26 pub content: BTreeMap<String, RefOr<Content>>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub required: Option<Required>,
32
33 #[serde(skip_serializing_if = "Option::is_none", flatten)]
35 pub extensions: Option<Extensions>,
36 }
37}
38
39impl RequestBody {
40 pub fn new() -> Self {
42 Default::default()
43 }
44}
45
46impl RequestBodyBuilder {
47 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
49 set_value!(self description description.map(|description| description.into()))
50 }
51
52 pub fn required(mut self, required: Option<Required>) -> Self {
54 set_value!(self required required)
55 }
56
57 pub fn content<S: Into<String>, C: Into<Content>>(
59 mut self,
60 content_type: S,
61 content: C,
62 ) -> Self {
63 self.content
64 .insert(content_type.into(), content.into().into());
65
66 self
67 }
68
69 pub fn content_ref<S: Into<String>>(
71 mut self,
72 content_type: S,
73 content_ref: super::Ref,
74 ) -> Self {
75 self.content
76 .insert(content_type.into(), RefOr::Ref(content_ref));
77
78 self
79 }
80
81 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
83 set_value!(self extensions extensions)
84 }
85}
86
87#[cfg(feature = "openapi_extensions")]
116#[cfg_attr(doc_cfg, doc(cfg(feature = "openapi_extensions")))]
117pub trait RequestBodyExt {
118 fn json_schema_ref(self, ref_name: &str) -> Self;
121}
122
123#[cfg(feature = "openapi_extensions")]
124impl RequestBodyExt for RequestBody {
125 fn json_schema_ref(mut self, ref_name: &str) -> RequestBody {
126 self.content.insert(
127 "application/json".to_string(),
128 crate::openapi::Content::new(Some(crate::openapi::Ref::from_schema_name(ref_name)))
129 .into(),
130 );
131 self
132 }
133}
134
135#[cfg(feature = "openapi_extensions")]
136impl RequestBodyExt for RequestBodyBuilder {
137 fn json_schema_ref(self, ref_name: &str) -> RequestBodyBuilder {
138 self.content(
139 "application/json",
140 crate::openapi::Content::new(Some(crate::openapi::Ref::from_schema_name(ref_name))),
141 )
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::{Content, RequestBody, RequestBodyBuilder, Required};
148 use insta::assert_json_snapshot;
149
150 #[test]
151 fn request_body_new() {
152 let request_body = RequestBody::new();
153
154 assert!(request_body.content.is_empty());
155 assert_eq!(request_body.description, None);
156 assert!(request_body.required.is_none());
157 }
158
159 #[test]
160 fn request_body_builder() {
161 let request_body = RequestBodyBuilder::new()
162 .description(Some("A sample requestBody"))
163 .required(Some(Required::True))
164 .content(
165 "application/json",
166 Content::new(Some(crate::openapi::Ref::from_schema_name("EmailPayload"))),
167 )
168 .build();
169 assert_json_snapshot!(request_body);
170 }
171}
172
173#[cfg(all(test, feature = "openapi_extensions"))]
174#[cfg_attr(doc_cfg, doc(cfg(feature = "openapi_extensions")))]
175mod openapi_extensions_tests {
176 use crate::openapi::request_body::RequestBodyBuilder;
177 use insta::assert_json_snapshot;
178
179 use super::RequestBodyExt;
180
181 #[test]
182 fn request_body_ext() {
183 let request_body = RequestBodyBuilder::new()
184 .build()
185 .json_schema_ref("EmailPayload");
187 assert_json_snapshot!(request_body);
188 }
189
190 #[test]
191 fn request_body_builder_ext() {
192 let request_body = RequestBodyBuilder::new()
193 .json_schema_ref("EmailPayload")
194 .build();
195 assert_json_snapshot!(request_body);
196 }
197}