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")]
167 pub allow_reserved: Option<bool>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
172 example: Option<Value>,
173
174 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
177 pub examples: PropMap<String, RefOr<Example>>,
178
179 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
184 pub content: PropMap<String, Content>,
185
186 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
188 pub extensions: PropMap<String, serde_json::Value>,
189}
190
191impl Parameter {
192 #[must_use]
194 pub fn new<S: Into<String>>(name: S) -> Self {
195 Self {
196 name: name.into(),
197 required: Required::Unset,
198 ..Default::default()
199 }
200 }
201 #[must_use]
203 pub fn name<I: Into<String>>(mut self, name: I) -> Self {
204 self.name = name.into();
205 self
206 }
207
208 #[must_use]
212 pub fn location(mut self, location: ParameterIn) -> Self {
213 self.parameter_in = location;
214 if self.parameter_in == ParameterIn::Path {
215 self.required = Required::True;
216 }
217 self
218 }
219
220 #[deprecated(since = "0.94.0", note = "use `Parameter::location` instead")]
222 #[must_use]
223 pub fn parameter_in(self, parameter_in: ParameterIn) -> Self {
224 self.location(parameter_in)
225 }
226
227 pub fn merge(&mut self, other: Self) -> bool {
230 let Self {
231 name,
232 parameter_in,
233 description,
234 required,
235 deprecated,
236 allow_empty_value,
237 schema,
238 style,
239 explode,
240 allow_reserved,
241 example,
242 examples,
243 content,
244 extensions,
245 } = other;
246 if name != self.name || parameter_in != self.parameter_in {
247 return false;
248 }
249 if let Some(description) = description {
250 self.description = Some(description);
251 }
252
253 if required != Required::Unset {
254 self.required = required;
255 }
256 if self.parameter_in == ParameterIn::Path {
258 self.required = Required::True;
259 }
260
261 if let Some(deprecated) = deprecated {
262 self.deprecated = Some(deprecated);
263 }
264 if let Some(allow_empty_value) = allow_empty_value {
265 self.allow_empty_value = Some(allow_empty_value);
266 }
267 if let Some(schema) = schema {
268 self.schema = Some(schema);
269 }
270 if let Some(style) = style {
271 self.style = Some(style);
272 }
273 if let Some(explode) = explode {
274 self.explode = Some(explode);
275 }
276 if let Some(allow_reserved) = allow_reserved {
277 self.allow_reserved = Some(allow_reserved);
278 }
279 if let Some(example) = example {
280 self.example = Some(example);
281 }
282 for (k, v) in examples {
283 self.examples.insert(k, v);
284 }
285 for (k, v) in content {
286 self.content.insert(k, v);
287 }
288
289 self.extensions.extend(extensions);
290 true
291 }
292
293 #[must_use]
296 pub fn required(mut self, required: impl Into<Required>) -> Self {
297 self.required = required.into();
298 if self.parameter_in == ParameterIn::Path {
300 self.required = Required::True;
301 }
302
303 self
304 }
305
306 #[must_use]
308 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
309 self.description = Some(description.into());
310 self
311 }
312
313 #[must_use]
315 pub fn deprecated<D: Into<Deprecated>>(mut self, deprecated: D) -> Self {
316 self.deprecated = Some(deprecated.into());
317 self
318 }
319
320 #[must_use]
322 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
323 self.schema = Some(component.into());
324 self
325 }
326
327 #[must_use]
329 pub fn style(mut self, style: ParameterStyle) -> Self {
330 self.style = Some(style);
331 self
332 }
333
334 #[must_use]
336 pub fn explode(mut self, explode: bool) -> Self {
337 self.explode = Some(explode);
338 self
339 }
340
341 #[must_use]
343 pub fn allow_reserved(mut self, allow_reserved: bool) -> Self {
344 self.allow_reserved = Some(allow_reserved);
345 self
346 }
347
348 #[must_use]
350 pub fn example(mut self, example: Value) -> Self {
351 self.example = Some(example);
352 self
353 }
354
355 #[must_use]
359 pub fn add_example<N: Into<String>, E: Into<RefOr<Example>>>(
360 mut self,
361 name: N,
362 example: E,
363 ) -> Self {
364 self.examples.insert(name.into(), example.into());
365 self
366 }
367
368 #[must_use]
370 pub fn examples<I, N, E>(mut self, examples: I) -> Self
371 where
372 I: IntoIterator<Item = (N, E)>,
373 N: Into<String>,
374 E: Into<RefOr<Example>>,
375 {
376 self.examples = examples
377 .into_iter()
378 .map(|(name, example)| (name.into(), example.into()))
379 .collect();
380 self
381 }
382
383 #[must_use]
388 pub fn content<S: Into<String>, C: Into<Content>>(mut self, media_type: S, content: C) -> Self {
389 self.content.insert(media_type.into(), content.into());
390 self
391 }
392
393 #[must_use]
397 pub fn allow_empty_value(mut self, allow: bool) -> Self {
398 self.allow_empty_value = Some(allow);
399 self
400 }
401}
402
403#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, Copy, Debug)]
406#[serde(rename_all = "lowercase")]
407pub enum ParameterIn {
408 Query,
412 #[serde(rename = "querystring")]
421 QueryString,
422 #[default]
424 Path,
425 Header,
427 Cookie,
429}
430
431#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
433#[serde(rename_all = "camelCase")]
434pub enum ParameterStyle {
435 Matrix,
439 Label,
443 Form,
447 Simple,
450 SpaceDelimited,
453 PipeDelimited,
456 DeepObject,
459 Cookie,
465}
466
467#[cfg(test)]
468mod tests {
469 use assert_json_diff::assert_json_eq;
470 use serde_json::json;
471
472 use super::*;
473 use crate::Object;
474
475 #[test]
476 fn test_build_parameter() {
477 let parameter = Parameter::new("name");
478 assert_eq!(parameter.name, "name");
479
480 let parameter = parameter
481 .name("new name")
482 .location(ParameterIn::Query)
483 .required(Required::True)
484 .description("description")
485 .deprecated(Deprecated::False)
486 .schema(Schema::object(Object::new()))
487 .style(ParameterStyle::Simple)
488 .explode(true)
489 .allow_reserved(true)
490 .example(Value::String("example".to_owned()));
491 assert_json_eq!(
492 parameter,
493 json!({
494 "name": "new name",
495 "in": "query",
496 "required": true,
497 "description": "description",
498 "deprecated": false,
499 "schema": {
500 "type": "object"
501 },
502 "style": "simple",
503 "explode": true,
504 "allowReserved": true,
505 "example": "example"
506 })
507 );
508 }
509
510 #[test]
511 fn test_parameter_merge_fail() {
512 let mut parameter1 = Parameter::new("param1");
513 let parameter2 = Parameter::new("param2");
514
515 assert!(!parameter1.merge(parameter2));
516 }
517
518 #[test]
519 fn test_parameter_merge_success() {
520 let mut parameter1 = Parameter::new("param1");
521 let mut parameter2 = Parameter::new("param1")
522 .description("description")
523 .required(Required::True)
524 .deprecated(Deprecated::True)
525 .schema(Schema::object(Object::new()))
526 .style(ParameterStyle::Form)
527 .explode(true)
528 .allow_reserved(true)
529 .example(Value::String("example".to_owned()));
530
531 parameter1.extensions =
532 PropMap::from([("key1".to_owned(), Value::String("value1".to_owned()))]);
533 parameter2.extensions =
534 PropMap::from([("key2".to_owned(), Value::String("value2".to_owned()))]);
535
536 assert!(parameter1.merge(parameter2));
537 assert_json_eq!(
538 parameter1,
539 json!({
540 "name": "param1",
541 "in": "path",
542 "description": "description",
543 "required": true,
544 "deprecated": true,
545 "schema": {
546 "type": "object"
547 },
548 "style": "form",
549 "explode": true,
550 "allowReserved": true,
551 "example": "example",
552 "key1": "value1",
553 "key2": "value2"
554 })
555 )
556 }
557
558 #[test]
559 fn test_parameter_merge_no_extensions() {
560 let mut parameter1 = Parameter::new("param1");
561 let mut parameter2 = Parameter::new("param1")
562 .description("description")
563 .required(Required::True)
564 .deprecated(Deprecated::True)
565 .schema(Schema::object(Object::new()))
566 .style(ParameterStyle::Form)
567 .explode(true)
568 .allow_reserved(true)
569 .example(Value::String("example".to_owned()));
570
571 parameter2.extensions =
572 PropMap::from([("key2".to_owned(), Value::String("value2".to_owned()))]);
573
574 assert!(parameter1.merge(parameter2));
575 assert_json_eq!(
576 parameter1,
577 json!({
578 "name": "param1",
579 "in": "path",
580 "description": "description",
581 "required": true,
582 "deprecated": true,
583 "schema": {
584 "type": "object"
585 },
586 "style": "form",
587 "explode": true,
588 "allowReserved": true,
589 "example": "example",
590 "key2": "value2",
591 })
592 )
593 }
594
595 #[test]
596 fn test_build_parameters() {
597 let parameters = Parameters::new();
598 assert!(parameters.is_empty());
599 }
600
601 #[test]
602 fn parameter_in_querystring_round_trips() {
603 let parameter = Parameter::new("query")
604 .location(ParameterIn::QueryString)
605 .content(
606 "application/x-www-form-urlencoded",
607 crate::Content::new(Schema::object(Object::new())),
608 );
609
610 let value = serde_json::to_value(¶meter).expect("serialize");
611 assert_eq!(value["in"], json!("querystring"));
612
613 let parsed: Parameter = serde_json::from_value(value).expect("deserialize");
614 assert_eq!(parsed.parameter_in, ParameterIn::QueryString);
615 }
616
617 #[test]
618 fn parameter_style_cookie_round_trips() {
619 let parameter = Parameter::new("session")
620 .location(ParameterIn::Cookie)
621 .style(ParameterStyle::Cookie);
622
623 let value = serde_json::to_value(¶meter).expect("serialize");
624 assert_eq!(value["style"], json!("cookie"));
625
626 let parsed: Parameter = serde_json::from_value(value).expect("deserialize");
627 assert_eq!(parsed.style, Some(ParameterStyle::Cookie));
628 }
629
630 #[test]
631 fn querystring_and_query_parameters_are_distinct_entries() {
632 let mut parameters = Parameters::new();
633 parameters.insert(Parameter::new("q").location(ParameterIn::Query));
634 parameters.insert(Parameter::new("q").location(ParameterIn::QueryString));
635
636 assert!(parameters.contains("q", ParameterIn::Query));
637 assert!(parameters.contains("q", ParameterIn::QueryString));
638 assert_eq!(parameters.0.len(), 2);
639 }
640
641 #[test]
642 fn test_parameters_into_iter() {
643 let parameters = Parameters::new().parameter(Parameter::new("param"));
644 let mut iter = parameters.into_iter();
645 assert_eq!(
649 iter.next(),
650 Some(Parameter::new("param").required(Required::True))
651 );
652 assert!(iter.next().is_none());
653 }
654
655 #[test]
656 fn test_parameters_contain() {
657 let parameters = Parameters::new().parameter(Parameter::new("param"));
658 assert!(parameters.contains("param", ParameterIn::Path));
659 }
660
661 #[test]
662 fn test_parameters_insert_existed_item() {
663 let mut parameters = Parameters::new();
664 parameters.insert(Parameter::new("param"));
665 assert!(parameters.contains("param", ParameterIn::Path));
666
667 parameters.insert(Parameter::new("param"));
668 assert_eq!(parameters.0.len(), 1);
669 }
670
671 #[test]
672 fn test_parameters_append() {
673 let mut parameters1 = Parameters::new().parameter(Parameter::new("param1"));
674 let mut parameters2 = Parameters::new().parameter(Parameter::new("param2"));
675
676 parameters1.append(&mut parameters2);
677 assert_json_eq!(
678 parameters1,
679 json!([
680 {
681 "in": "path",
682 "name": "param1",
683 "required": true
684 },
685 {
686 "in": "path",
687 "name": "param2",
688 "required": true
689 }
690 ])
691 );
692 }
693
694 #[test]
695 fn parameter_required_unset_is_omitted() {
696 let parameter = Parameter::new("filter").location(ParameterIn::Query);
697 let value = serde_json::to_value(¶meter).expect("serialize");
698 assert!(
699 value.get("required").is_none(),
700 "expected `required` to be omitted when Unset; got: {value}"
701 );
702 }
703
704 #[test]
705 fn parameter_required_true_is_emitted_for_path_in() {
706 let parameter = Parameter::new("id").location(ParameterIn::Path);
707 let value = serde_json::to_value(¶meter).expect("serialize");
709 assert_eq!(value["required"], serde_json::Value::Bool(true));
710 }
711
712 #[test]
713 fn parameter_with_examples_serializes_under_camel_case_field() {
714 use crate::Example;
715
716 let parameter = Parameter::new("filter")
717 .location(ParameterIn::Query)
718 .add_example("first", Example::new().value(json!("foo")))
719 .add_example("second", Example::new().value(json!("bar")));
720
721 let value = serde_json::to_value(¶meter).expect("serialize");
722 assert_eq!(
723 value["examples"],
724 json!({
725 "first": { "value": "foo" },
726 "second": { "value": "bar" }
727 })
728 );
729 }
730
731 #[test]
732 fn parameter_with_content_serializes_as_media_type_map() {
733 let parameter = Parameter::new("filter")
734 .location(ParameterIn::Query)
735 .content(
736 "application/json",
737 Content::new(RefOr::Ref(crate::Ref::from_schema_name("Filter"))),
738 );
739
740 let value = serde_json::to_value(¶meter).expect("serialize");
741 assert_eq!(
742 value["content"],
743 json!({
744 "application/json": {
745 "schema": { "$ref": "#/components/schemas/Filter" }
746 }
747 })
748 );
749 }
750
751 #[test]
752 fn parameter_allow_empty_value_round_trips_under_camel_case() {
753 let parameter = Parameter::new("flag")
754 .location(ParameterIn::Query)
755 .allow_empty_value(true);
756
757 let value = serde_json::to_value(¶meter).expect("serialize");
758 assert_eq!(value["allowEmptyValue"], serde_json::Value::Bool(true));
759 }
760}