1pub use super::extensions::{
4 Coder, Coders, MediaRange, JSON_CODER, JSON_MIME, YAML_CODER, YAML_MIME,
5};
6
7use super::schema::Schema;
8use crate::error::ValidationError;
9use once_cell::sync::Lazy;
10use paperclip_macros::api_v2_schema_struct;
11use regex::{Captures, Regex};
12use serde::ser::{SerializeMap, Serializer};
13
14#[cfg(feature = "actix-base")]
15use actix_web::http::Method;
16
17use std::{
18 borrow::Cow,
19 collections::{BTreeMap, BTreeSet},
20 fmt::{self, Display},
21 ops::{Deref, DerefMut},
22 sync::{Arc, RwLock},
23};
24
25static PATH_TEMPLATE_REGEX: Lazy<Regex> =
27 Lazy::new(|| Regex::new(r"\{(.*?)\}").expect("path template regex"));
28
29const SPECIAL_HEADERS: &[&str] = &["content-type", "accept", "authorization"];
32
33#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
35pub enum Version {
36 #[serde(rename = "2.0")]
37 V2,
38}
39
40#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
42#[serde(rename_all = "lowercase")]
43pub enum DataType {
44 Integer,
45 Number,
46 String,
47 Boolean,
48 Array,
49 Object,
50 File,
51}
52
53impl DataType {
54 #[inline]
56 pub fn is_primitive(self) -> bool {
57 std::matches!(
58 self,
59 DataType::Integer | DataType::Number | DataType::String | DataType::Boolean
60 )
61 }
62}
63
64#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
66#[serde(rename_all = "lowercase")]
67pub enum DataTypeFormat {
68 Int32,
69 Int64,
70 Float,
71 Double,
72 Byte,
73 Binary,
74 Date,
75 #[serde(rename = "date-time")]
76 DateTime,
77 Password,
78 Url,
79 Uuid,
80 Ip,
81 IpV4,
82 IpV6,
83 #[serde(other)]
84 Other,
85}
86
87#[allow(clippy::to_string_trait_impl)]
88impl ToString for DataTypeFormat {
89 fn to_string(&self) -> String {
90 match self {
91 DataTypeFormat::Int32 => "int32",
92 DataTypeFormat::Int64 => "int64",
93 DataTypeFormat::Float => "float",
94 DataTypeFormat::Double => "double",
95 DataTypeFormat::Byte => "byte",
96 DataTypeFormat::Binary => "binary",
97 DataTypeFormat::Date => "date",
98 DataTypeFormat::DateTime => "datetime",
99 DataTypeFormat::Password => "password",
100 DataTypeFormat::Url => "url",
101 DataTypeFormat::Uuid => "uuid",
102 DataTypeFormat::Ip => "ip",
103 DataTypeFormat::IpV4 => "ipv4",
104 DataTypeFormat::IpV6 => "ipv6",
105 DataTypeFormat::Other => "other",
107 }
108 .to_string()
109 }
110}
111
112impl From<DataTypeFormat> for DataType {
113 fn from(src: DataTypeFormat) -> Self {
114 match src {
115 DataTypeFormat::Int32 => Self::Integer,
116 DataTypeFormat::Int64 => Self::Integer,
117 DataTypeFormat::Float => Self::Number,
118 DataTypeFormat::Double => Self::Number,
119 DataTypeFormat::Byte => Self::String,
120 DataTypeFormat::Binary => Self::String,
121 DataTypeFormat::Date => Self::String,
122 DataTypeFormat::DateTime => Self::String,
123 DataTypeFormat::Password => Self::String,
124 DataTypeFormat::Url => Self::String,
125 DataTypeFormat::Uuid => Self::String,
126 DataTypeFormat::Ip => Self::String,
127 DataTypeFormat::IpV4 => Self::String,
128 DataTypeFormat::IpV6 => Self::String,
129 DataTypeFormat::Other => Self::Object,
130 }
131 }
132}
133
134pub type ResolvableApi<S> = Api<ResolvableParameter<S>, ResolvableResponse<S>, Resolvable<S>>;
136
137pub type DefaultApiRaw = Api<DefaultParameterRaw, DefaultResponseRaw, DefaultSchemaRaw>;
139
140fn strip_templates_from_paths<P: serde::ser::Serialize, R: serde::ser::Serialize, S: Serializer>(
141 tree: &BTreeMap<String, PathItem<P, R>>,
142 serializer: S,
143) -> Result<S::Ok, S::Error> {
144 let len = tree.len();
145 let mut map = serializer.serialize_map(Some(len))?;
146 for (k, v) in tree {
147 let path = strip_pattern_from_template(k);
148 map.serialize_entry(&path, v)?;
149 }
150 map.end()
151}
152
153fn strip_pattern_from_template(path: &str) -> String {
154 let mut clean_path = path.to_string();
155 for cap in PATH_TEMPLATE_REGEX.captures_iter(path) {
156 let name_only = cap[1]
157 .split_once(':')
158 .map(|t| t.0.to_string())
159 .unwrap_or_else(|| cap[1].to_string());
160 if cap[1] != name_only {
161 clean_path = clean_path.replace(
162 format!("{{{}}}", &cap[1]).as_str(),
163 format!("{{{}}}", name_only).as_str(),
164 );
165 }
166 }
167 clean_path
168}
169
170#[derive(Clone, Debug, Default, Serialize, Deserialize)]
174pub struct Api<P, R, S> {
175 pub swagger: Version,
176 #[serde(default = "BTreeMap::new")]
177 pub definitions: BTreeMap<String, S>,
178 #[serde(serialize_with = "strip_templates_from_paths")]
179 pub paths: BTreeMap<String, PathItem<P, R>>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub host: Option<String>,
182 #[serde(rename = "basePath", skip_serializing_if = "Option::is_none")]
183 pub base_path: Option<String>,
184 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
185 pub consumes: BTreeSet<MediaRange>,
186 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
187 pub produces: BTreeSet<MediaRange>,
188 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
189 pub schemes: BTreeSet<OperationProtocol>,
190 #[serde(default = "BTreeMap::new", skip_serializing_if = "BTreeMap::is_empty")]
191 pub parameters: BTreeMap<String, P>,
192 #[serde(default = "BTreeMap::new", skip_serializing_if = "BTreeMap::is_empty")]
193 pub responses: BTreeMap<String, R>,
194 #[serde(
195 default,
196 rename = "securityDefinitions",
197 skip_serializing_if = "BTreeMap::is_empty"
198 )]
199 pub security_definitions: BTreeMap<String, SecurityScheme>,
200 #[serde(default, skip_serializing_if = "Vec::is_empty")]
201 pub security: Vec<BTreeMap<String, BTreeSet<String>>>,
202 #[serde(default, skip_serializing_if = "Vec::is_empty")]
203 pub tags: Vec<Tag>,
204 #[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
205 pub external_docs: Option<ExternalDocs>,
206 #[serde(
219 default,
220 rename = "x-rust-coders",
221 skip_serializing_if = "<Coders as Deref>::Target::is_empty"
222 )]
223 pub coders: Coders,
224 #[serde(
236 default,
237 rename = "x-rust-dependencies",
238 skip_serializing_if = "BTreeMap::is_empty"
239 )]
240 pub support_crates: BTreeMap<String, String>,
241 #[serde(skip)]
244 pub spec_format: SpecFormat,
245 pub info: Info,
246
247 #[serde(
248 flatten,
249 skip_serializing_if = "BTreeMap::is_empty",
250 deserialize_with = "crate::v2::extensions::deserialize_extensions"
251 )]
252 pub extensions: BTreeMap<String, serde_json::Value>,
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum SpecFormat {
258 Json,
259 Yaml,
260}
261
262impl SpecFormat {
263 pub fn coder(self) -> Arc<Coder> {
265 match self {
266 SpecFormat::Json => JSON_CODER.clone(),
267 SpecFormat::Yaml => YAML_CODER.clone(),
268 }
269 }
270
271 pub fn mime(self) -> &'static MediaRange {
273 match self {
274 SpecFormat::Json => &JSON_MIME,
275 SpecFormat::Yaml => &YAML_MIME,
276 }
277 }
278}
279
280impl<P, R, S> Api<P, R, S> {
281 pub fn path_parameters_map(
284 path: &str,
285 mut f: impl FnMut(&str) -> Cow<'static, str>,
286 ) -> Cow<'_, str> {
287 PATH_TEMPLATE_REGEX.replace_all(path, |c: &Captures| f(&c[1]))
288 }
289}
290
291use crate as paperclip; #[api_v2_schema_struct]
297#[derive(Clone, Debug, Serialize, Deserialize)]
298pub struct DefaultSchema;
299
300#[derive(Clone, Debug, Default, Serialize, Deserialize)]
304pub struct Info {
305 pub version: String,
306 pub title: String,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub description: Option<String>,
309 #[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
310 pub terms_of_service: Option<String>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub contact: Option<Contact>,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub license: Option<License>,
315 #[serde(
317 flatten,
318 skip_serializing_if = "BTreeMap::is_empty",
319 deserialize_with = "crate::v2::extensions::deserialize_extensions"
320 )]
321 pub extensions: BTreeMap<String, serde_json::Value>,
322}
323
324#[derive(Clone, Debug, Default, Serialize, Deserialize)]
328pub struct Contact {
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub name: Option<String>,
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub url: Option<String>,
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub email: Option<String>,
335}
336
337#[derive(Clone, Debug, Default, Serialize, Deserialize)]
341pub struct License {
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub name: Option<String>,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub url: Option<String>,
346}
347
348#[derive(Clone, Debug, Default, Serialize, Deserialize)]
352pub struct SecurityScheme {
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub name: Option<String>,
355 #[serde(rename = "type")]
356 pub type_: String,
357 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
358 pub in_: Option<String>,
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub flow: Option<String>,
361 #[serde(rename = "authorizationUrl", skip_serializing_if = "Option::is_none")]
362 pub auth_url: Option<String>,
363 #[serde(rename = "tokenUrl", skip_serializing_if = "Option::is_none")]
364 pub token_url: Option<String>,
365 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
366 pub scopes: BTreeMap<String, String>,
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub description: Option<String>,
369}
370
371impl SecurityScheme {
372 pub fn update_definitions(mut self, name: &str, map: &mut BTreeMap<String, SecurityScheme>) {
374 if let Some(existing) = map.get_mut(name) {
375 existing.name = existing.name.take().or(self.name);
376 if !self.type_.is_empty() {
377 existing.type_ = self.type_;
378 }
379 existing.in_ = existing.in_.take().or(self.in_);
380 existing.flow = existing.flow.take().or(self.flow);
381 existing.auth_url = existing.auth_url.take().or(self.auth_url);
382 existing.token_url = existing.token_url.take().or(self.token_url);
383 existing.scopes.append(&mut self.scopes);
384 existing.description = existing.description.take().or(self.description);
385 return;
386 }
387
388 map.insert(name.into(), self);
389 }
390
391 pub fn append_map(
393 old: BTreeMap<String, SecurityScheme>,
394 new: &mut BTreeMap<String, SecurityScheme>,
395 ) {
396 for (name, def) in old {
397 def.update_definitions(&name, new);
398 }
399 }
400}
401
402#[derive(Clone, Debug, Default, Serialize, Deserialize)]
406pub struct Tag {
407 pub name: String,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub description: Option<String>,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 #[serde(rename = "externalDocs")]
412 pub external_docs: Option<ExternalDocs>,
413}
414
415#[derive(Clone, Debug, Default, Serialize, Deserialize)]
419pub struct ExternalDocs {
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub description: Option<String>,
422 pub url: String,
423}
424
425pub type ResolvablePathItem<S> = PathItem<ResolvableParameter<S>, ResolvableResponse<S>>;
427
428pub type DefaultPathItemRaw = PathItem<DefaultParameterRaw, DefaultResponseRaw>;
430
431#[derive(Clone, Debug, Default, Serialize, Deserialize)]
435pub struct PathItem<P, R> {
436 #[serde(flatten, default = "BTreeMap::default")]
437 pub methods: BTreeMap<HttpMethod, Operation<P, R>>,
438 #[serde(default = "Vec::default", skip_serializing_if = "Vec::is_empty")]
439 pub parameters: Vec<Either<Reference, P>>,
440}
441
442impl<S> PathItem<Parameter<S>, Response<S>> {
443 pub fn normalize(&mut self) {
447 let mut shared_params = None;
451 for op in self.methods.values() {
452 let params = op
453 .parameters
454 .iter()
455 .map(|p| p.name.clone())
456 .collect::<BTreeSet<_>>();
457 if let Some(p) = shared_params.take() {
458 shared_params = Some(&p & ¶ms); } else {
460 shared_params = Some(params);
461 }
462 }
463
464 let shared_params = match shared_params {
465 Some(p) => p,
466 None => return,
467 };
468
469 for name in &shared_params {
473 for op in self.methods.values_mut() {
474 let idx = op
475 .parameters
476 .iter()
477 .position(|p| p.name == name.as_str())
478 .expect("collected parameter missing?");
479 let p = op.parameters.swap_remove(idx);
480 if !self.parameters.iter().any(|p| p.name == name.as_str()) {
481 self.parameters.push(p);
482 }
483 }
484 }
485 }
486}
487
488pub type ResolvableParameter<S> = Arc<RwLock<Parameter<Resolvable<S>>>>;
490
491pub type DefaultParameterRaw = Parameter<DefaultSchemaRaw>;
493
494#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
498#[serde(rename_all = "camelCase")]
499pub struct Parameter<S> {
500 #[serde(skip_serializing_if = "Option::is_none")]
501 pub description: Option<String>,
502 #[serde(rename = "in")]
503 pub in_: ParameterIn,
504 pub name: String,
505 #[serde(default, skip_serializing_if = "is_false")]
506 pub required: bool,
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub schema: Option<S>,
509 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
510 pub data_type: Option<DataType>,
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub format: Option<DataTypeFormat>,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub items: Option<Items>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub collection_format: Option<CollectionFormat>,
517 #[serde(default, skip_serializing_if = "is_false")]
518 pub allow_empty_value: bool,
519 #[serde(skip_serializing_if = "Option::is_none")]
520 pub default: Option<serde_json::Value>,
521 #[serde(skip_serializing_if = "Option::is_none")]
522 pub maximum: Option<f32>,
523 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "Option::is_none")]
524 pub exclusive_maximum: Option<bool>,
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub minimum: Option<f32>,
527 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "Option::is_none")]
528 pub exclusive_minimum: Option<bool>,
529 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
530 pub max_length: Option<u32>,
531 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
532 pub min_length: Option<u32>,
533 #[serde(skip_serializing_if = "Option::is_none")]
534 pub pattern: Option<String>,
535 #[serde(rename = "maxItems", skip_serializing_if = "Option::is_none")]
536 pub max_items: Option<u32>,
537 #[serde(rename = "minItems", skip_serializing_if = "Option::is_none")]
538 pub min_items: Option<u32>,
539 #[serde(default, rename = "uniqueItems", skip_serializing_if = "is_false")]
540 pub unique_items: bool,
541 #[serde(rename = "multipleOf", skip_serializing_if = "Option::is_none")]
542 pub multiple_of: Option<f32>,
543 #[serde(default, rename = "enum", skip_serializing_if = "Vec::is_empty")]
544 pub enum_: Vec<serde_json::Value>,
545}
546
547#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
551#[serde(rename_all = "camelCase")]
552pub struct Items {
553 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
554 pub data_type: Option<DataType>,
555 #[serde(skip_serializing_if = "Option::is_none")]
556 pub format: Option<DataTypeFormat>,
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub items: Option<Box<Items>>,
559 #[serde(skip_serializing_if = "Option::is_none")]
560 pub collection_format: Option<CollectionFormat>,
561 #[serde(default, rename = "enum", skip_serializing_if = "Vec::is_empty")]
562 pub enum_: Vec<serde_json::Value>,
563 #[serde(skip_serializing_if = "Option::is_none")]
564 pub maximum: Option<f32>,
565 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "Option::is_none")]
566 pub exclusive_maximum: Option<bool>,
567 #[serde(skip_serializing_if = "Option::is_none")]
568 pub minimum: Option<f32>,
569 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "Option::is_none")]
570 pub exclusive_minimum: Option<bool>,
571 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
572 pub max_length: Option<u32>,
573 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
574 pub min_length: Option<u32>,
575 #[serde(skip_serializing_if = "Option::is_none")]
576 pub pattern: Option<String>,
577 #[serde(rename = "maxItems", skip_serializing_if = "Option::is_none")]
578 pub max_items: Option<u32>,
579 #[serde(rename = "minItems", skip_serializing_if = "Option::is_none")]
580 pub min_items: Option<u32>,
581 #[serde(rename = "uniqueItems", skip_serializing_if = "Option::is_none")]
582 pub unique_items: Option<bool>,
583 #[serde(rename = "multipleOf", skip_serializing_if = "Option::is_none")]
584 pub multiple_of: Option<f32>,
585}
586
587impl<S> Parameter<Resolvable<S>>
588where
589 S: Schema,
590{
591 pub fn check(&self, path: &str) -> Result<(), ValidationError> {
594 if self.in_ == ParameterIn::Body {
595 if self.schema.is_none() {
597 return Err(ValidationError::MissingSchemaForBodyParameter(
598 self.name.clone(),
599 path.into(),
600 ));
601 }
602
603 return Ok(());
604 } else if self.in_ == ParameterIn::Header {
605 let lower = self.name.to_lowercase();
607 if SPECIAL_HEADERS.iter().any(|&h| lower == h) {
608 return Err(ValidationError::InvalidHeader(
609 self.name.clone(),
610 path.into(),
611 ));
612 }
613 }
614
615 let mut is_invalid = false;
617 match self.data_type {
618 Some(dt) if dt.is_primitive() => (),
619 Some(DataType::Array) => {
620 let mut inner = self.items.as_ref();
621 loop {
622 let dt = inner.as_ref().and_then(|s| s.data_type);
623 match dt {
624 Some(ty) if ty.is_primitive() => break,
625 Some(DataType::Array) => {
626 inner = inner.as_ref().and_then(|s| s.items.as_deref());
627 }
628 None => {
629 return Err(ValidationError::InvalidParameterType(
630 self.name.clone(),
631 path.into(),
632 dt,
633 self.in_,
634 ));
635 }
636 _ => {
637 is_invalid = true;
638 break;
639 }
640 }
641 }
642 }
643 Some(DataType::File) => {
645 if self.in_ != ParameterIn::FormData {
647 is_invalid = true;
648 }
649 }
650 _ => is_invalid = true,
651 }
652
653 if is_invalid {
654 return Err(ValidationError::InvalidParameterType(
655 self.name.clone(),
656 path.into(),
657 self.data_type,
658 self.in_,
659 ));
660 }
661
662 Ok(())
663 }
664}
665
666#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
668#[serde(rename_all = "camelCase")]
669pub enum ParameterIn {
670 Query,
671 Header,
672 Path,
673 FormData,
674 Body,
675}
676
677#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
679#[serde(rename_all = "lowercase")]
680pub enum CollectionFormat {
681 Csv,
682 Ssv,
683 Tsv,
684 Pipes,
685 Multi,
686}
687
688pub type ResolvableOperation<S> = Operation<ResolvableParameter<S>, ResolvableResponse<S>>;
690
691pub type DefaultOperationRaw = Operation<DefaultParameterRaw, DefaultResponseRaw>;
693
694#[derive(Clone, Debug, Default, Serialize, Deserialize)]
698#[serde(rename_all = "camelCase")]
699pub struct Operation<P, R> {
700 #[serde(skip_serializing_if = "Option::is_none")]
701 pub operation_id: Option<String>,
702 #[serde(skip_serializing_if = "Option::is_none")]
703 pub summary: Option<String>,
704 #[serde(skip_serializing_if = "Option::is_none")]
705 pub description: Option<String>,
706 #[serde(default, skip_serializing_if = "Option::is_none")]
710 pub consumes: Option<BTreeSet<MediaRange>>,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
712 pub produces: Option<BTreeSet<MediaRange>>,
713 #[serde(default, skip_serializing_if = "Vec::is_empty")]
714 pub security: Vec<BTreeMap<String, Vec<String>>>,
715 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
716 pub schemes: BTreeSet<OperationProtocol>,
717 pub responses: BTreeMap<String, Either<Reference, R>>,
719 #[serde(default = "Vec::default", skip_serializing_if = "Vec::is_empty")]
720 pub parameters: Vec<Either<Reference, P>>,
721 #[serde(default, skip_serializing_if = "is_false")]
722 pub deprecated: bool,
723 #[serde(default, skip_serializing_if = "Vec::is_empty")]
724 pub tags: Vec<String>,
725}
726
727impl<S> Operation<Parameter<S>, Response<S>> {
728 pub fn set_parameter_names_from_path_template(&mut self, path: &str) {
731 let mut names = vec![];
732 Api::<(), (), ()>::path_parameters_map(path, |name| {
733 if self
734 .parameters
735 .iter()
736 .filter(|p| p.in_ == ParameterIn::Path)
737 .all(|p| p.name != name)
738 {
739 names.push(name.to_owned());
740 }
741
742 ":".into()
743 });
744
745 for p in self
746 .parameters
747 .iter_mut()
748 .filter(|p| p.in_ == ParameterIn::Path)
749 .filter(|p| p.name.is_empty())
750 .rev()
751 {
752 if let Some(n) = names.pop() {
753 if let Some((name, pattern)) = n.split_once(':') {
754 p.name = name.to_string();
755 p.pattern = Some(pattern.to_string());
756 } else {
757 p.name = n;
758 }
759 } else {
760 break;
761 }
762 }
763 }
764}
765
766#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
770pub struct Reference {
771 #[serde(rename = "$ref")]
772 pub reference: String,
773}
774
775#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
777#[serde(rename_all = "lowercase")]
778pub enum OperationProtocol {
779 Http,
780 Https,
781 Ws,
782 Wss,
783}
784
785pub type ResolvableResponse<S> = Arc<RwLock<Response<Resolvable<S>>>>;
787
788pub type DefaultResponseRaw = Response<DefaultSchemaRaw>;
790
791#[derive(Clone, Debug, Default, Serialize, Deserialize)]
795pub struct Response<S> {
796 #[serde(skip_serializing_if = "Option::is_none")]
797 pub description: Option<String>,
798 #[serde(skip_serializing_if = "Option::is_none")]
799 pub schema: Option<S>,
800 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
801 pub headers: BTreeMap<String, Header>,
802}
803
804#[derive(Clone, Debug, Default, Serialize, Deserialize)]
808pub struct Header {
809 #[serde(skip_serializing_if = "Option::is_none")]
810 pub description: Option<String>,
811 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
812 pub data_type: Option<DataType>,
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub format: Option<DataTypeFormat>,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub items: Option<Items>,
817 #[serde(skip_serializing_if = "Option::is_none")]
818 pub collection_format: Option<CollectionFormat>,
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub default: Option<serde_json::Value>,
821 #[serde(default, rename = "enum", skip_serializing_if = "Vec::is_empty")]
822 pub enum_: Vec<serde_json::Value>,
823 #[serde(skip_serializing_if = "Option::is_none")]
824 pub maximum: Option<f32>,
825 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "Option::is_none")]
826 pub exclusive_maximum: Option<bool>,
827 #[serde(skip_serializing_if = "Option::is_none")]
828 pub minimum: Option<f32>,
829 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "Option::is_none")]
830 pub exclusive_minimum: Option<bool>,
831 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
832 pub max_length: Option<u32>,
833 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
834 pub min_length: Option<u32>,
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub pattern: Option<String>,
837 #[serde(rename = "maxItems", skip_serializing_if = "Option::is_none")]
838 pub max_items: Option<u32>,
839 #[serde(rename = "minItems", skip_serializing_if = "Option::is_none")]
840 pub min_items: Option<u32>,
841 #[serde(rename = "uniqueItems", skip_serializing_if = "Option::is_none")]
842 pub unique_items: Option<bool>,
843 #[serde(rename = "multipleOf", skip_serializing_if = "Option::is_none")]
844 pub multiple_of: Option<f32>,
845}
846
847#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
849#[serde(rename_all = "lowercase")]
850pub enum HttpMethod {
851 Get,
852 Put,
853 Post,
854 Delete,
855 Options,
856 Head,
857 Patch,
858}
859
860impl HttpMethod {
861 pub fn allows_body(self) -> bool {
863 std::matches!(self, HttpMethod::Post | HttpMethod::Put | HttpMethod::Patch)
864 }
865}
866
867#[derive(Debug, Clone, Serialize, Deserialize)]
872#[serde(untagged)]
873pub enum Either<L, R> {
874 Left(L),
875 Right(R),
876}
877
878impl<L, R> Either<L, R> {
879 pub fn right(&self) -> Option<&R> {
881 match self {
882 Either::Left(_) => None,
883 Either::Right(r) => Some(r),
884 }
885 }
886
887 pub fn right_mut(&mut self) -> Option<&mut R> {
889 match self {
890 Either::Left(_) => None,
891 Either::Right(r) => Some(r),
892 }
893 }
894
895 pub fn left(&self) -> Option<&L> {
897 match self {
898 Either::Left(l) => Some(l),
899 Either::Right(_) => None,
900 }
901 }
902
903 pub fn left_mut(&mut self) -> Option<&mut L> {
905 match self {
906 Either::Left(l) => Some(l),
907 Either::Right(_) => None,
908 }
909 }
910}
911
912#[derive(Debug, Deserialize)]
916#[serde(untagged)]
917pub enum Resolvable<S> {
918 Raw(Arc<RwLock<S>>),
919 #[serde(skip)]
920 Resolved {
921 new: Arc<RwLock<S>>,
922 old: Arc<RwLock<S>>,
923 },
924}
925
926impl<S> Resolvable<S>
927where
928 S: Schema,
929{
930 pub fn get_description(&self) -> Option<String> {
932 match *self {
933 Resolvable::Raw(ref s) => s.read().unwrap().description().map(String::from),
934 Resolvable::Resolved { ref old, .. } => {
936 old.read().unwrap().description().map(String::from)
937 }
938 }
939 }
940}
941
942impl Default for SpecFormat {
945 fn default() -> Self {
946 SpecFormat::Json
947 }
948}
949
950#[cfg(feature = "actix-base")]
951impl From<&Method> for HttpMethod {
952 fn from(method: &Method) -> HttpMethod {
953 match method.as_str() {
954 "PUT" => HttpMethod::Put,
955 "POST" => HttpMethod::Post,
956 "DELETE" => HttpMethod::Delete,
957 "OPTIONS" => HttpMethod::Options,
958 "HEAD" => HttpMethod::Head,
959 "PATCH" => HttpMethod::Patch,
960 _ => HttpMethod::Get,
961 }
962 }
963}
964
965impl<T> Deref for Either<Reference, T> {
966 type Target = T;
967
968 fn deref(&self) -> &Self::Target {
969 match *self {
970 Either::Left(_) => panic!("unable to deref because reference is not resolved."),
971 Either::Right(ref r) => r,
972 }
973 }
974}
975
976impl<T> DerefMut for Either<Reference, T> {
977 fn deref_mut(&mut self) -> &mut Self::Target {
978 match *self {
979 Either::Left(_) => panic!("unable to deref because reference is not resolved."),
980 Either::Right(ref mut r) => r,
981 }
982 }
983}
984
985impl<S> Deref for Resolvable<S> {
986 type Target = Arc<RwLock<S>>;
987
988 fn deref(&self) -> &Self::Target {
989 match *self {
990 Resolvable::Raw(ref s) => s,
991 Resolvable::Resolved { ref new, .. } => new,
992 }
993 }
994}
995
996impl<S> DerefMut for Resolvable<S> {
997 fn deref_mut(&mut self) -> &mut Self::Target {
998 match *self {
999 Resolvable::Raw(ref mut s) => s,
1000 Resolvable::Resolved { ref mut new, .. } => new,
1001 }
1002 }
1003}
1004
1005impl<S: Default> Default for Resolvable<S> {
1006 fn default() -> Self {
1007 Resolvable::from(S::default())
1008 }
1009}
1010
1011impl<S> From<S> for Resolvable<S> {
1012 fn from(t: S) -> Self {
1013 Resolvable::Raw(Arc::new(RwLock::new(t)))
1014 }
1015}
1016
1017impl<S> Clone for Resolvable<S> {
1018 fn clone(&self) -> Self {
1019 match *self {
1020 Resolvable::Raw(ref s) => Resolvable::Raw(s.clone()),
1021 Resolvable::Resolved { ref new, ref old } => Resolvable::Resolved {
1022 new: new.clone(),
1023 old: old.clone(),
1024 },
1025 }
1026 }
1027}
1028
1029impl Display for HttpMethod {
1030 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1031 write!(f, "{:?}", self)
1032 }
1033}
1034
1035impl Default for Version {
1036 fn default() -> Self {
1037 Version::V2
1038 }
1039}
1040
1041impl Default for CollectionFormat {
1042 fn default() -> Self {
1043 CollectionFormat::Csv
1044 }
1045}
1046
1047impl Default for ParameterIn {
1049 fn default() -> Self {
1050 ParameterIn::Body
1051 }
1052}
1053
1054#[allow(clippy::trivially_copy_pass_by_ref)]
1057fn is_false(val: &bool) -> bool {
1058 !*val
1059}