1use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use super::content::Content;
8use super::example::Example;
9use super::{Deprecated, RefOr, Required, Schema};
10use crate::PropMap;
11
12fn is_required_unset(value: &Required) -> bool {
13 matches!(value, Required::Unset)
14}
15
16#[derive(Serialize, Deserialize, Debug, PartialEq, Default, Clone)]
18pub struct Parameters(pub Vec<Parameter>);
19
20impl IntoIterator for Parameters {
21 type Item = Parameter;
22 type IntoIter = <Vec<Parameter> as IntoIterator>::IntoIter;
23
24 fn into_iter(self) -> Self::IntoIter {
25 self.0.into_iter()
26 }
27}
28
29impl Parameters {
30 #[must_use]
33 pub fn new() -> Self {
34 Default::default()
35 }
36 #[must_use]
38 pub fn is_empty(&self) -> bool {
39 self.0.is_empty()
40 }
41 #[must_use]
43 pub fn parameter<P: Into<Parameter>>(mut self, parameter: P) -> Self {
44 self.insert(parameter);
45 self
46 }
47 #[must_use]
49 pub fn contains(&self, name: &str, parameter_in: ParameterIn) -> bool {
50 self.0
51 .iter()
52 .any(|item| item.name == name && item.parameter_in == parameter_in)
53 }
54 pub fn insert<P: Into<Parameter>>(&mut self, parameter: P) {
56 let mut parameter = parameter.into();
57 if parameter.parameter_in == ParameterIn::Path {
59 parameter.required = Required::True;
60 }
61 let exist_item = self.0.iter_mut().find(|item| {
62 item.name == parameter.name && item.parameter_in == parameter.parameter_in
63 });
64
65 if let Some(exist_item) = exist_item {
66 exist_item.merge(parameter);
67 } else {
68 self.0.push(parameter);
69 }
70 }
71 pub fn append(&mut self, other: &mut Self) {
76 for item in other.0.drain(..) {
77 self.insert(item);
78 }
79 }
80 pub fn extend<I>(&mut self, iter: I)
82 where
83 I: IntoIterator<Item = Parameter>,
84 {
85 for item in iter {
86 self.insert(item);
87 }
88 }
89}
90
91#[non_exhaustive]
95#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
96#[serde(rename_all = "camelCase")]
97pub struct Parameter {
98 pub name: String,
103
104 #[serde(rename = "in")]
106 pub parameter_in: ParameterIn,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub description: Option<String>,
111
112 #[serde(default, skip_serializing_if = "is_required_unset")]
117 pub required: Required,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub deprecated: Option<Deprecated>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
129 pub allow_empty_value: Option<bool>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
136 pub schema: Option<RefOr<Schema>>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
141 pub style: Option<ParameterStyle>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
156 pub explode: Option<bool>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
162 pub allow_reserved: Option<bool>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
167 example: Option<Value>,
168
169 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
172 pub examples: PropMap<String, RefOr<Example>>,
173
174 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
179 pub content: PropMap<String, Content>,
180
181 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
183 pub extensions: PropMap<String, serde_json::Value>,
184}
185
186impl Parameter {
187 #[must_use]
189 pub fn new<S: Into<String>>(name: S) -> Self {
190 Self {
191 name: name.into(),
192 required: Required::Unset,
193 ..Default::default()
194 }
195 }
196 #[must_use]
198 pub fn name<I: Into<String>>(mut self, name: I) -> Self {
199 self.name = name.into();
200 self
201 }
202
203 #[must_use]
207 pub fn location(mut self, location: ParameterIn) -> Self {
208 self.parameter_in = location;
209 if self.parameter_in == ParameterIn::Path {
210 self.required = Required::True;
211 }
212 self
213 }
214
215 #[deprecated(since = "0.94.0", note = "use `Parameter::location` instead")]
217 #[must_use]
218 pub fn parameter_in(self, parameter_in: ParameterIn) -> Self {
219 self.location(parameter_in)
220 }
221
222 pub fn merge(&mut self, other: Self) -> bool {
225 let Self {
226 name,
227 parameter_in,
228 description,
229 required,
230 deprecated,
231 allow_empty_value,
232 schema,
233 style,
234 explode,
235 allow_reserved,
236 example,
237 examples,
238 content,
239 extensions,
240 } = other;
241 if name != self.name || parameter_in != self.parameter_in {
242 return false;
243 }
244 if let Some(description) = description {
245 self.description = Some(description);
246 }
247
248 if required != Required::Unset {
249 self.required = required;
250 }
251 if self.parameter_in == ParameterIn::Path {
253 self.required = Required::True;
254 }
255
256 if let Some(deprecated) = deprecated {
257 self.deprecated = Some(deprecated);
258 }
259 if let Some(allow_empty_value) = allow_empty_value {
260 self.allow_empty_value = Some(allow_empty_value);
261 }
262 if let Some(schema) = schema {
263 self.schema = Some(schema);
264 }
265 if let Some(style) = style {
266 self.style = Some(style);
267 }
268 if let Some(explode) = explode {
269 self.explode = Some(explode);
270 }
271 if let Some(allow_reserved) = allow_reserved {
272 self.allow_reserved = Some(allow_reserved);
273 }
274 if let Some(example) = example {
275 self.example = Some(example);
276 }
277 for (k, v) in examples {
278 self.examples.insert(k, v);
279 }
280 for (k, v) in content {
281 self.content.insert(k, v);
282 }
283
284 self.extensions.extend(extensions);
285 true
286 }
287
288 #[must_use]
291 pub fn required(mut self, required: impl Into<Required>) -> Self {
292 self.required = required.into();
293 if self.parameter_in == ParameterIn::Path {
295 self.required = Required::True;
296 }
297
298 self
299 }
300
301 #[must_use]
303 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
304 self.description = Some(description.into());
305 self
306 }
307
308 #[must_use]
310 pub fn deprecated<D: Into<Deprecated>>(mut self, deprecated: D) -> Self {
311 self.deprecated = Some(deprecated.into());
312 self
313 }
314
315 #[must_use]
317 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
318 self.schema = Some(component.into());
319 self
320 }
321
322 #[must_use]
324 pub fn style(mut self, style: ParameterStyle) -> Self {
325 self.style = Some(style);
326 self
327 }
328
329 #[must_use]
331 pub fn explode(mut self, explode: bool) -> Self {
332 self.explode = Some(explode);
333 self
334 }
335
336 #[must_use]
338 pub fn allow_reserved(mut self, allow_reserved: bool) -> Self {
339 self.allow_reserved = Some(allow_reserved);
340 self
341 }
342
343 #[must_use]
345 pub fn example(mut self, example: Value) -> Self {
346 self.example = Some(example);
347 self
348 }
349
350 #[must_use]
354 pub fn add_example<N: Into<String>, E: Into<RefOr<Example>>>(
355 mut self,
356 name: N,
357 example: E,
358 ) -> Self {
359 self.examples.insert(name.into(), example.into());
360 self
361 }
362
363 #[must_use]
365 pub fn examples<I, N, E>(mut self, examples: I) -> Self
366 where
367 I: IntoIterator<Item = (N, E)>,
368 N: Into<String>,
369 E: Into<RefOr<Example>>,
370 {
371 self.examples = examples
372 .into_iter()
373 .map(|(name, example)| (name.into(), example.into()))
374 .collect();
375 self
376 }
377
378 #[must_use]
383 pub fn content<S: Into<String>, C: Into<Content>>(mut self, media_type: S, content: C) -> Self {
384 self.content.insert(media_type.into(), content.into());
385 self
386 }
387
388 #[must_use]
392 pub fn allow_empty_value(mut self, allow: bool) -> Self {
393 self.allow_empty_value = Some(allow);
394 self
395 }
396}
397
398#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, Copy, Debug)]
401#[serde(rename_all = "lowercase")]
402pub enum ParameterIn {
403 Query,
405 #[default]
407 Path,
408 Header,
410 Cookie,
412}
413
414#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
416#[serde(rename_all = "camelCase")]
417pub enum ParameterStyle {
418 Matrix,
422 Label,
426 Form,
430 Simple,
433 SpaceDelimited,
436 PipeDelimited,
439 DeepObject,
442}
443
444#[cfg(test)]
445mod tests {
446 use assert_json_diff::assert_json_eq;
447 use serde_json::json;
448
449 use super::*;
450 use crate::Object;
451
452 #[test]
453 fn test_build_parameter() {
454 let parameter = Parameter::new("name");
455 assert_eq!(parameter.name, "name");
456
457 let parameter = parameter
458 .name("new name")
459 .location(ParameterIn::Query)
460 .required(Required::True)
461 .description("description")
462 .deprecated(Deprecated::False)
463 .schema(Schema::object(Object::new()))
464 .style(ParameterStyle::Simple)
465 .explode(true)
466 .allow_reserved(true)
467 .example(Value::String("example".to_owned()));
468 assert_json_eq!(
469 parameter,
470 json!({
471 "name": "new name",
472 "in": "query",
473 "required": true,
474 "description": "description",
475 "deprecated": false,
476 "schema": {
477 "type": "object"
478 },
479 "style": "simple",
480 "explode": true,
481 "allowReserved": true,
482 "example": "example"
483 })
484 );
485 }
486
487 #[test]
488 fn test_parameter_merge_fail() {
489 let mut parameter1 = Parameter::new("param1");
490 let parameter2 = Parameter::new("param2");
491
492 assert!(!parameter1.merge(parameter2));
493 }
494
495 #[test]
496 fn test_parameter_merge_success() {
497 let mut parameter1 = Parameter::new("param1");
498 let mut parameter2 = Parameter::new("param1")
499 .description("description")
500 .required(Required::True)
501 .deprecated(Deprecated::True)
502 .schema(Schema::object(Object::new()))
503 .style(ParameterStyle::Form)
504 .explode(true)
505 .allow_reserved(true)
506 .example(Value::String("example".to_owned()));
507
508 parameter1.extensions =
509 PropMap::from([("key1".to_owned(), Value::String("value1".to_owned()))]);
510 parameter2.extensions =
511 PropMap::from([("key2".to_owned(), Value::String("value2".to_owned()))]);
512
513 assert!(parameter1.merge(parameter2));
514 assert_json_eq!(
515 parameter1,
516 json!({
517 "name": "param1",
518 "in": "path",
519 "description": "description",
520 "required": true,
521 "deprecated": true,
522 "schema": {
523 "type": "object"
524 },
525 "style": "form",
526 "explode": true,
527 "allowReserved": true,
528 "example": "example",
529 "key1": "value1",
530 "key2": "value2"
531 })
532 )
533 }
534
535 #[test]
536 fn test_parameter_merge_no_extensions() {
537 let mut parameter1 = Parameter::new("param1");
538 let mut parameter2 = Parameter::new("param1")
539 .description("description")
540 .required(Required::True)
541 .deprecated(Deprecated::True)
542 .schema(Schema::object(Object::new()))
543 .style(ParameterStyle::Form)
544 .explode(true)
545 .allow_reserved(true)
546 .example(Value::String("example".to_owned()));
547
548 parameter2.extensions =
549 PropMap::from([("key2".to_owned(), Value::String("value2".to_owned()))]);
550
551 assert!(parameter1.merge(parameter2));
552 assert_json_eq!(
553 parameter1,
554 json!({
555 "name": "param1",
556 "in": "path",
557 "description": "description",
558 "required": true,
559 "deprecated": true,
560 "schema": {
561 "type": "object"
562 },
563 "style": "form",
564 "explode": true,
565 "allowReserved": true,
566 "example": "example",
567 "key2": "value2",
568 })
569 )
570 }
571
572 #[test]
573 fn test_build_parameters() {
574 let parameters = Parameters::new();
575 assert!(parameters.is_empty());
576 }
577
578 #[test]
579 fn test_parameters_into_iter() {
580 let parameters = Parameters::new().parameter(Parameter::new("param"));
581 let mut iter = parameters.into_iter();
582 assert_eq!(
586 iter.next(),
587 Some(Parameter::new("param").required(Required::True))
588 );
589 assert!(iter.next().is_none());
590 }
591
592 #[test]
593 fn test_parameters_contain() {
594 let parameters = Parameters::new().parameter(Parameter::new("param"));
595 assert!(parameters.contains("param", ParameterIn::Path));
596 }
597
598 #[test]
599 fn test_parameters_insert_existed_item() {
600 let mut parameters = Parameters::new();
601 parameters.insert(Parameter::new("param"));
602 assert!(parameters.contains("param", ParameterIn::Path));
603
604 parameters.insert(Parameter::new("param"));
605 assert_eq!(parameters.0.len(), 1);
606 }
607
608 #[test]
609 fn test_parameters_append() {
610 let mut parameters1 = Parameters::new().parameter(Parameter::new("param1"));
611 let mut parameters2 = Parameters::new().parameter(Parameter::new("param2"));
612
613 parameters1.append(&mut parameters2);
614 assert_json_eq!(
615 parameters1,
616 json!([
617 {
618 "in": "path",
619 "name": "param1",
620 "required": true
621 },
622 {
623 "in": "path",
624 "name": "param2",
625 "required": true
626 }
627 ])
628 );
629 }
630
631 #[test]
632 fn parameter_required_unset_is_omitted() {
633 let parameter = Parameter::new("filter").location(ParameterIn::Query);
634 let value = serde_json::to_value(¶meter).expect("serialize");
635 assert!(
636 value.get("required").is_none(),
637 "expected `required` to be omitted when Unset; got: {value}"
638 );
639 }
640
641 #[test]
642 fn parameter_required_true_is_emitted_for_path_in() {
643 let parameter = Parameter::new("id").location(ParameterIn::Path);
644 let value = serde_json::to_value(¶meter).expect("serialize");
646 assert_eq!(value["required"], serde_json::Value::Bool(true));
647 }
648
649 #[test]
650 fn parameter_with_examples_serializes_under_camel_case_field() {
651 use crate::Example;
652
653 let parameter = Parameter::new("filter")
654 .location(ParameterIn::Query)
655 .add_example("first", Example::new().value(json!("foo")))
656 .add_example("second", Example::new().value(json!("bar")));
657
658 let value = serde_json::to_value(¶meter).expect("serialize");
659 assert_eq!(
660 value["examples"],
661 json!({
662 "first": { "value": "foo" },
663 "second": { "value": "bar" }
664 })
665 );
666 }
667
668 #[test]
669 fn parameter_with_content_serializes_as_media_type_map() {
670 let parameter = Parameter::new("filter")
671 .location(ParameterIn::Query)
672 .content(
673 "application/json",
674 Content::new(RefOr::Ref(crate::Ref::from_schema_name("Filter"))),
675 );
676
677 let value = serde_json::to_value(¶meter).expect("serialize");
678 assert_eq!(
679 value["content"],
680 json!({
681 "application/json": {
682 "schema": { "$ref": "#/components/schemas/Filter" }
683 }
684 })
685 );
686 }
687
688 #[test]
689 fn parameter_allow_empty_value_round_trips_under_camel_case() {
690 let parameter = Parameter::new("flag")
691 .location(ParameterIn::Query)
692 .allow_empty_value(true);
693
694 let value = serde_json::to_value(¶meter).expect("serialize");
695 assert_eq!(value["allowEmptyValue"], serde_json::Value::Bool(true));
696 }
697}