1use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::BTreeMap;
8
9use super::{
10 builder, content::Content, example::Example, extensions::Extensions, path::ParameterStyle,
11 set_value, Deprecated, Object, RefOr, Schema, Type,
12};
13
14builder! {
15 HeaderBuilder;
16
17 #[non_exhaustive]
21 #[derive(Serialize, Deserialize, Clone, PartialEq)]
22 #[cfg_attr(feature = "debug", derive(Debug))]
23 pub struct Header {
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub schema: Option<RefOr<Schema>>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub description: Option<String>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub deprecated: Option<Deprecated>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub style: Option<ParameterStyle>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub explode: Option<bool>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub example: Option<Value>,
47
48 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
50 pub examples: BTreeMap<String, RefOr<Example>>,
51
52 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
54 pub content: BTreeMap<String, Content>,
55
56 #[serde(skip_serializing_if = "Option::is_none", flatten)]
58 pub extensions: Option<Extensions>,
59 }
60}
61
62impl Header {
63 pub fn new<C: Into<RefOr<Schema>>>(component: C) -> Self {
81 Self {
82 schema: Some(component.into()),
83 ..Default::default()
84 }
85 }
86}
87
88impl Default for Header {
89 fn default() -> Self {
90 Self {
91 schema: Some(Object::with_type(Type::String).into()),
92 description: None,
93 deprecated: None,
94 style: None,
95 explode: None,
96 example: None,
97 examples: BTreeMap::new(),
98 content: BTreeMap::new(),
99 extensions: None,
100 }
101 }
102}
103
104impl HeaderBuilder {
105 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
107 set_value!(self schema Some(component.into()))
108 }
109
110 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
112 set_value!(self description description.map(|description| description.into()))
113 }
114
115 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
117 set_value!(self deprecated deprecated)
118 }
119
120 pub fn style(mut self, style: Option<ParameterStyle>) -> Self {
122 set_value!(self style style)
123 }
124
125 pub fn explode(mut self, explode: Option<bool>) -> Self {
127 set_value!(self explode explode)
128 }
129
130 pub fn example(mut self, example: Option<Value>) -> Self {
132 set_value!(self example example)
133 }
134
135 pub fn examples_from_iter<
137 E: IntoIterator<Item = (N, V)>,
138 N: Into<String>,
139 V: Into<RefOr<Example>>,
140 >(
141 mut self,
142 examples: E,
143 ) -> Self {
144 self.examples.extend(
145 examples
146 .into_iter()
147 .map(|(name, example)| (name.into(), example.into())),
148 );
149
150 self
151 }
152
153 pub fn content_from_iter<E: IntoIterator<Item = (N, V)>, N: Into<String>, V: Into<Content>>(
155 mut self,
156 content: E,
157 ) -> Self {
158 self.content.extend(
159 content
160 .into_iter()
161 .map(|(name, content)| (name.into(), content.into())),
162 );
163
164 self
165 }
166
167 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
169 set_value!(self extensions extensions)
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use serde_json::json;
177
178 use crate::openapi::content::ContentBuilder;
179 use crate::openapi::example::ExampleBuilder;
180
181 #[test]
182 fn test_header_builder_and_serialization() {
183 let header = HeaderBuilder::new()
184 .description(Some("custom header"))
185 .deprecated(Some(Deprecated::True))
186 .style(Some(ParameterStyle::Simple))
187 .explode(Some(true))
188 .example(Some(json!("example-value")))
189 .build();
190
191 insta::assert_json_snapshot!(&header, @r#"
192 {
193 "schema": {
194 "type": "string"
195 },
196 "description": "custom header",
197 "deprecated": true,
198 "style": "simple",
199 "explode": true,
200 "example": "example-value"
201 }
202 "#);
203 }
204
205 #[test]
206 fn test_header_with_content_and_examples() {
207 let content = ContentBuilder::new()
208 .schema(Some(Object::with_type(Type::Integer)))
209 .build();
210 let example = ExampleBuilder::new().value(Some(json!("test"))).build();
211
212 let header = Header {
213 schema: None,
214 content: BTreeMap::from_iter([("application/json".to_string(), content)]),
215 examples: BTreeMap::from_iter([("test_example".to_string(), example.into())]),
216 ..Default::default()
217 };
218
219 insta::assert_json_snapshot!(&header, @r#"
220 {
221 "examples": {
222 "test_example": {
223 "value": "test"
224 }
225 },
226 "content": {
227 "application/json": {
228 "schema": {
229 "type": "integer"
230 }
231 }
232 }
233 }
234 "#);
235 }
236}