kynos_openapi/model/parameter/
header.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::{
7 Map,
8 model::{
9 body::media_type::MediaType,
10 example::{Example, Examples, examples_from, examples_into, examples_with_named},
11 extensions::Extensions,
12 parameter::{ParameterConflict, shape_from, style::HeaderStyle},
13 reference::{Ref, RefOr},
14 schema::Schema,
15 },
16};
17
18pub const IGNORED_HEADER_PARAMETERS: &[&str] = &["Accept", "Content-Type", "Authorization"];
25
26#[must_use]
30pub fn is_ignored_header_parameter(name: &str) -> bool {
31 IGNORED_HEADER_PARAMETERS
32 .iter()
33 .any(|ignored| ignored.eq_ignore_ascii_case(name))
34}
35
36pub const IGNORED_HEADERS: &[&str] = &["Content-Type"];
44
45#[must_use]
49pub fn is_ignored_header(name: &str) -> bool {
50 IGNORED_HEADERS
51 .iter()
52 .any(|ignored| ignored.eq_ignore_ascii_case(name))
53}
54
55#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
61#[serde(try_from = "RawHeader", into = "RawHeader")]
62pub struct Header {
63 pub description: Option<String>,
67
68 pub required: Option<bool>,
70
71 pub deprecated: Option<bool>,
73
74 shape: HeaderShape,
75
76 examples: Option<Examples>,
77
78 pub extensions: Extensions,
80}
81
82#[derive(Clone, Debug, PartialEq)]
95pub enum HeaderShape {
96 Schema {
98 schema: Schema,
100
101 style: Option<HeaderStyle>,
103
104 explode: Option<bool>,
106 },
107
108 Content {
114 media_type: String,
116
117 value: Box<MediaType>,
119 },
120}
121
122impl Header {
123 #[must_use]
125 pub fn new(schema: Schema) -> Self {
126 Self::shaped(HeaderShape::Schema {
127 schema,
128 style: None,
129 explode: None,
130 })
131 }
132
133 pub fn with_content(media_type: impl Into<String>, value: MediaType) -> Self {
135 Self::shaped(HeaderShape::Content {
136 media_type: media_type.into(),
137 value: Box::new(value),
138 })
139 }
140
141 fn shaped(shape: HeaderShape) -> Self {
142 Self {
143 description: None,
144 required: None,
145 deprecated: None,
146 shape,
147 examples: None,
148 extensions: Extensions::default(),
149 }
150 }
151
152 #[must_use]
154 pub fn shape(&self) -> &HeaderShape {
155 &self.shape
156 }
157
158 pub fn shape_mut(&mut self) -> &mut HeaderShape {
164 &mut self.shape
165 }
166
167 #[must_use]
169 pub fn schema(&self) -> Option<&Schema> {
170 match &self.shape {
171 HeaderShape::Schema { schema, .. } => Some(schema),
172 HeaderShape::Content { .. } => None,
173 }
174 }
175
176 #[must_use]
178 pub fn content(&self) -> Option<(&str, &MediaType)> {
179 match &self.shape {
180 HeaderShape::Content { media_type, value } => Some((media_type, &**value)),
181 HeaderShape::Schema { .. } => None,
182 }
183 }
184
185 #[must_use]
187 pub fn style(&self) -> Option<HeaderStyle> {
188 match self.shape {
189 HeaderShape::Schema { style, .. } => style,
190 HeaderShape::Content { .. } => None,
191 }
192 }
193
194 #[must_use]
203 pub fn with_style(mut self, style: HeaderStyle, explode: bool) -> Self {
204 if let HeaderShape::Schema {
205 style: slot,
206 explode: exploded,
207 ..
208 } = &mut self.shape
209 {
210 *slot = Some(style);
211 *exploded = Some(explode);
212 }
213 self
214 }
215
216 #[must_use]
218 pub fn examples(&self) -> Option<&Examples> {
219 self.examples.as_ref()
220 }
221
222 #[must_use]
224 pub fn example(&self) -> Option<&Value> {
225 match &self.examples {
226 Some(Examples::Inline(value)) => Some(value),
227 Some(Examples::Named(_)) | None => None,
228 }
229 }
230
231 #[must_use]
233 pub fn named_examples(&self) -> Option<&Map<RefOr<Example>>> {
234 match &self.examples {
235 Some(Examples::Named(named)) => Some(named),
236 Some(Examples::Inline(_)) | None => None,
237 }
238 }
239
240 #[must_use]
245 pub fn with_example(mut self, value: impl Into<Value>) -> Self {
246 self.examples = Some(Examples::Inline(value.into()));
247 self
248 }
249
250 #[must_use]
252 pub fn with_named_example(mut self, name: impl Into<String>, example: Example) -> Self {
253 self.examples = Some(examples_with_named(
254 self.examples,
255 name.into(),
256 RefOr::Item(example),
257 ));
258 self
259 }
260
261 #[must_use]
264 pub fn with_named_example_ref(mut self, name: impl Into<String>, example: Ref) -> Self {
265 self.examples = Some(examples_with_named(
266 self.examples,
267 name.into(),
268 RefOr::Ref(example),
269 ));
270 self
271 }
272
273 #[must_use]
275 pub fn with_description(mut self, description: impl Into<String>) -> Self {
276 self.description = Some(description.into());
277 self
278 }
279
280 #[must_use]
282 pub fn required(mut self, required: bool) -> Self {
283 self.required = Some(required);
284 self
285 }
286}
287
288#[derive(Serialize, Deserialize)]
290struct RawHeader {
291 #[serde(default, skip_serializing_if = "Option::is_none")]
292 description: Option<String>,
293
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 required: Option<bool>,
296
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 deprecated: Option<bool>,
299
300 #[serde(default, skip_serializing_if = "Option::is_none")]
301 style: Option<HeaderStyle>,
302
303 #[serde(default, skip_serializing_if = "Option::is_none")]
304 explode: Option<bool>,
305
306 #[serde(default, skip_serializing_if = "Option::is_none")]
307 schema: Option<Schema>,
308
309 #[serde(
310 default,
311 deserialize_with = "crate::model::nullable::some",
312 skip_serializing_if = "Option::is_none"
313 )]
314 example: Option<Value>,
315
316 #[serde(default, skip_serializing_if = "Option::is_none")]
317 examples: Option<Map<RefOr<Example>>>,
318
319 #[serde(default, skip_serializing_if = "Map::is_empty")]
320 content: Map<MediaType>,
321
322 #[serde(flatten)]
323 extensions: Extensions,
324}
325
326impl TryFrom<RawHeader> for Header {
327 type Error = ParameterConflict;
328
329 fn try_from(raw: RawHeader) -> Result<Self, Self::Error> {
330 let shape = match shape_from(raw.schema, raw.content)? {
331 (_, Some((media_type, value))) => HeaderShape::Content {
332 media_type,
333 value: Box::new(value),
334 },
335 (schema, None) => HeaderShape::Schema {
336 schema,
337 style: raw.style,
338 explode: raw.explode,
339 },
340 };
341
342 Ok(Self {
343 description: raw.description,
344 required: raw.required,
345 deprecated: raw.deprecated,
346 shape,
347 examples: examples_from(raw.example, raw.examples)?,
348 extensions: raw.extensions,
349 })
350 }
351}
352
353impl From<Header> for RawHeader {
354 fn from(header: Header) -> Self {
355 let (schema, style, explode, content) = match header.shape {
356 HeaderShape::Schema {
357 schema,
358 style,
359 explode,
360 } => (Some(schema), style, explode, Map::new()),
361 HeaderShape::Content { media_type, value } => {
362 (None, None, None, Map::from_iter([(media_type, *value)]))
363 }
364 };
365
366 let (example, examples) = examples_into(header.examples);
367
368 Self {
369 description: header.description,
370 required: header.required,
371 deprecated: header.deprecated,
372 style,
373 explode,
374 schema,
375 example,
376 examples,
377 content,
378 extensions: header.extensions,
379 }
380 }
381}