1use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use super::{Deprecated, RefOr, Required, Schema};
8use crate::PropMap;
9
10#[derive(Serialize, Deserialize, Debug, PartialEq, Default, Clone)]
12pub struct Parameters(pub Vec<Parameter>);
13
14impl IntoIterator for Parameters {
15 type Item = Parameter;
16 type IntoIter = <Vec<Parameter> as IntoIterator>::IntoIter;
17
18 fn into_iter(self) -> Self::IntoIter {
19 self.0.into_iter()
20 }
21}
22
23impl Parameters {
24 pub fn new() -> Self {
26 Default::default()
27 }
28 pub fn is_empty(&self) -> bool {
30 self.0.is_empty()
31 }
32 pub fn parameter<P: Into<Parameter>>(mut self, parameter: P) -> Self {
34 self.insert(parameter);
35 self
36 }
37 pub fn contains(&self, name: &str, parameter_in: ParameterIn) -> bool {
39 self.0
40 .iter()
41 .any(|item| item.name == name && item.parameter_in == parameter_in)
42 }
43 pub fn insert<P: Into<Parameter>>(&mut self, parameter: P) {
45 let parameter = parameter.into();
46 let exist_item = self.0.iter_mut().find(|item| {
47 item.name == parameter.name && item.parameter_in == parameter.parameter_in
48 });
49
50 if let Some(exist_item) = exist_item {
51 exist_item.merge(parameter);
52 } else {
53 self.0.push(parameter);
54 }
55 }
56 pub fn append(&mut self, other: &mut Parameters) {
61 for item in other.0.drain(..) {
62 self.insert(item);
63 }
64 }
65 pub fn extend<I>(&mut self, iter: I)
67 where
68 I: IntoIterator<Item = Parameter>,
69 {
70 for item in iter {
71 self.insert(item);
72 }
73 }
74}
75
76#[non_exhaustive]
80#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
81#[serde(rename_all = "camelCase")]
82pub struct Parameter {
83 pub name: String,
88
89 #[serde(rename = "in")]
91 pub parameter_in: ParameterIn,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub description: Option<String>,
96
97 pub required: Required,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub deprecated: Option<Deprecated>,
105 #[serde(skip_serializing_if = "Option::is_none")]
108 pub schema: Option<RefOr<Schema>>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
113 pub style: Option<ParameterStyle>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
128 pub explode: Option<bool>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
134 pub allow_reserved: Option<bool>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
139 example: Option<Value>,
140
141 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
143 pub extensions: PropMap<String, serde_json::Value>,
144}
145
146impl Parameter {
147 pub fn new<S: Into<String>>(name: S) -> Self {
149 Self {
150 name: name.into(),
151 required: Required::Unset,
152 ..Default::default()
153 }
154 }
155 pub fn name<I: Into<String>>(mut self, name: I) -> Self {
157 self.name = name.into();
158 self
159 }
160
161 pub fn parameter_in(mut self, parameter_in: ParameterIn) -> Self {
163 self.parameter_in = parameter_in;
164 if self.parameter_in == ParameterIn::Path {
165 self.required = Required::True;
166 }
167 self
168 }
169
170 pub fn merge(&mut self, other: Parameter) -> bool {
172 let Parameter {
173 name,
174 parameter_in,
175 description,
176 required,
177 deprecated,
178 schema,
179 style,
180 explode,
181 allow_reserved,
182 example,
183 extensions,
184 } = other;
185 if name != self.name || parameter_in != self.parameter_in {
186 return false;
187 }
188 if let Some(description) = description {
189 self.description = Some(description);
190 }
191
192 if required != Required::Unset {
193 self.required = required;
194 }
195
196 if let Some(deprecated) = deprecated {
197 self.deprecated = Some(deprecated);
198 }
199 if let Some(schema) = schema {
200 self.schema = Some(schema);
201 }
202 if let Some(style) = style {
203 self.style = Some(style);
204 }
205 if let Some(explode) = explode {
206 self.explode = Some(explode);
207 }
208 if let Some(allow_reserved) = allow_reserved {
209 self.allow_reserved = Some(allow_reserved);
210 }
211 if let Some(example) = example {
212 self.example = Some(example);
213 }
214
215 self.extensions.extend(extensions);
216 true
217 }
218
219 pub fn required(mut self, required: impl Into<Required>) -> Self {
222 self.required = required.into();
223 if self.parameter_in == ParameterIn::Path {
225 self.required = Required::True;
226 }
227
228 self
229 }
230
231 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
233 self.description = Some(description.into());
234 self
235 }
236
237 pub fn deprecated<D: Into<Deprecated>>(mut self, deprecated: D) -> Self {
239 self.deprecated = Some(deprecated.into());
240 self
241 }
242
243 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
245 self.schema = Some(component.into());
246 self
247 }
248
249 pub fn style(mut self, style: ParameterStyle) -> Self {
251 self.style = Some(style);
252 self
253 }
254
255 pub fn explode(mut self, explode: bool) -> Self {
257 self.explode = Some(explode);
258 self
259 }
260
261 pub fn allow_reserved(mut self, allow_reserved: bool) -> Self {
263 self.allow_reserved = Some(allow_reserved);
264 self
265 }
266
267 pub fn example(mut self, example: Value) -> Self {
269 self.example = Some(example);
270 self
271 }
272}
273
274#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Debug)]
276#[serde(rename_all = "lowercase")]
277pub enum ParameterIn {
278 Query,
280 Path,
282 Header,
284 Cookie,
286}
287
288impl Default for ParameterIn {
289 fn default() -> Self {
290 Self::Path
291 }
292}
293
294#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
296#[serde(rename_all = "camelCase")]
297pub enum ParameterStyle {
298 Matrix,
302 Label,
306 Form,
310 Simple,
313 SpaceDelimited,
316 PipeDelimited,
319 DeepObject,
322}
323
324#[cfg(test)]
325mod tests {
326 use assert_json_diff::assert_json_eq;
327 use serde_json::json;
328
329 use crate::Object;
330
331 use super::*;
332
333 #[test]
334 fn test_build_parameter() {
335 let parameter = Parameter::new("name");
336 assert_eq!(parameter.name, "name");
337
338 let parameter = parameter
339 .name("new name")
340 .parameter_in(ParameterIn::Query)
341 .required(Required::True)
342 .description("description")
343 .deprecated(Deprecated::False)
344 .schema(Schema::object(Object::new()))
345 .style(ParameterStyle::Simple)
346 .explode(true)
347 .allow_reserved(true)
348 .example(Value::String("example".to_string()));
349 assert_json_eq!(
350 parameter,
351 json!({
352 "name": "new name",
353 "in": "query",
354 "required": true,
355 "description": "description",
356 "deprecated": false,
357 "schema": {
358 "type": "object"
359 },
360 "style": "simple",
361 "explode": true,
362 "allowReserved": true,
363 "example": "example"
364 })
365 );
366 }
367
368 #[test]
369 fn test_parameter_merge_fail() {
370 let mut parameter1 = Parameter::new("param1");
371 let parameter2 = Parameter::new("param2");
372
373 assert!(!parameter1.merge(parameter2));
374 }
375
376 #[test]
377 fn test_parameter_merge_success() {
378 let mut parameter1 = Parameter::new("param1");
379 let mut parameter2 = Parameter::new("param1")
380 .description("description")
381 .required(Required::True)
382 .deprecated(Deprecated::True)
383 .schema(Schema::object(Object::new()))
384 .style(ParameterStyle::Form)
385 .explode(true)
386 .allow_reserved(true)
387 .example(Value::String("example".to_string()));
388
389 parameter1.extensions =
390 PropMap::from([("key1".to_string(), Value::String("value1".to_string()))]);
391 parameter2.extensions =
392 PropMap::from([("key2".to_string(), Value::String("value2".to_string()))]);
393
394 assert!(parameter1.merge(parameter2));
395 assert_json_eq!(
396 parameter1,
397 json!({
398 "name": "param1",
399 "in": "path",
400 "description": "description",
401 "required": true,
402 "deprecated": true,
403 "schema": {
404 "type": "object"
405 },
406 "style": "form",
407 "explode": true,
408 "allowReserved": true,
409 "example": "example",
410 "key1": "value1",
411 "key2": "value2"
412 })
413 )
414 }
415
416 #[test]
417 fn test_parameter_merge_no_extensions() {
418 let mut parameter1 = Parameter::new("param1");
419 let mut parameter2 = Parameter::new("param1")
420 .description("description")
421 .required(Required::True)
422 .deprecated(Deprecated::True)
423 .schema(Schema::object(Object::new()))
424 .style(ParameterStyle::Form)
425 .explode(true)
426 .allow_reserved(true)
427 .example(Value::String("example".to_string()));
428
429 parameter2.extensions =
430 PropMap::from([("key2".to_string(), Value::String("value2".to_string()))]);
431
432 assert!(parameter1.merge(parameter2));
433 assert_json_eq!(
434 parameter1,
435 json!({
436 "name": "param1",
437 "in": "path",
438 "description": "description",
439 "required": true,
440 "deprecated": true,
441 "schema": {
442 "type": "object"
443 },
444 "style": "form",
445 "explode": true,
446 "allowReserved": true,
447 "example": "example",
448 "key2": "value2",
449 })
450 )
451 }
452
453 #[test]
454 fn test_build_parameters() {
455 let parameters = Parameters::new();
456 assert!(parameters.is_empty());
457 }
458
459 #[test]
460 fn test_parameters_into_iter() {
461 let parameters = Parameters::new().parameter(Parameter::new("param"));
462 let mut iter = parameters.into_iter();
463 assert_eq!(iter.next(), Some(Parameter::new("param")));
464 assert!(iter.next().is_none());
465 }
466
467 #[test]
468 fn test_parameters_contain() {
469 let parameters = Parameters::new().parameter(Parameter::new("param"));
470 assert!(parameters.contains("param", ParameterIn::Path));
471 }
472
473 #[test]
474 fn test_parameters_insert_existed_item() {
475 let mut parameters = Parameters::new();
476 parameters.insert(Parameter::new("param"));
477 assert!(parameters.contains("param", ParameterIn::Path));
478
479 parameters.insert(Parameter::new("param"));
480 assert_eq!(parameters.0.len(), 1);
481 }
482
483 #[test]
484 fn test_parameters_append() {
485 let mut parameters1 = Parameters::new().parameter(Parameter::new("param1"));
486 let mut parameters2 = Parameters::new().parameter(Parameter::new("param2"));
487
488 parameters1.append(&mut parameters2);
489 assert_json_eq!(
490 parameters1,
491 json!([
492 {
493 "in": "path",
494 "name": "param1",
495 "required": false
496 },
497 {
498 "in": "path",
499 "name": "param2",
500 "required": false
501 }
502 ])
503 );
504 }
505}