1use indexmap::IndexMap;
2use serde_json::Value;
3
4use crate::core::{Either, ReferenceDescriptor};
5
6use crate::schema as core;
7
8use crate::schemas::openapi303::context::*;
9use crate::schemas::openapi303::schema::*;
10
11pub const VERSION: &str = "0.1.0";
12
13struct ConvertContext<'a> {
14 pub components: &'a Option<Components>,
15}
16
17impl From<OpenApi303> for core::HttpSchema {
18 fn from(spec: OpenApi303) -> Self {
19 let paths = {
20 let context = ConvertContext {
21 components: &spec.components,
22 };
23 spec.paths.map(|paths| convert_paths(paths, &context))
24 };
25
26 let components = if let Some(components) = spec.components {
27 let schemas = components.schemas.map(|schemas| {
28 schemas
29 .into_iter()
30 .map(|(key, schema_ref)| {
31 (key, convert_schema_ref(schema_ref))
32 })
33 .collect()
34 });
35
36 let responses = components.responses.map(|responses| {
37 responses
38 .into_iter()
39 .map(|(key, response_ref)| {
40 (key, convert_response_ref(response_ref))
41 })
42 .collect()
43 });
44
45 let parameters = components.parameters.map(|parameters| {
46 parameters
47 .into_iter()
48 .map(|(key, parameter_ref)| {
49 (key, convert_parameter_ref(parameter_ref))
50 })
51 .collect()
52 });
53
54 let examples = components.examples.map(|examples| {
55 examples
56 .into_iter()
57 .map(|(key, example_ref)| {
58 (key, convert_example_ref(example_ref))
59 })
60 .collect()
61 });
62
63 let request_bodies =
64 components.request_bodies.map(|request_bodies| {
65 request_bodies
66 .into_iter()
67 .map(|(key, request_body_ref)| {
68 (key, convert_request_body_ref(request_body_ref))
69 })
70 .collect()
71 });
72
73 let headers = components.headers.map(|headers| {
74 headers
75 .into_iter()
76 .map(|(key, header_ref)| {
77 (key, convert_header_ref(header_ref))
78 })
79 .collect()
80 });
81
82 let links = components.links.map(|links| {
83 links
84 .into_iter()
85 .map(|(key, link_ref)| (key, convert_link_ref(link_ref)))
86 .collect()
87 });
88
89 let security_schemes =
90 components.security_schemes.map(|security_schemes| {
91 security_schemes
92 .into_iter()
93 .map(|(key, security_scheme_ref)| {
94 (
95 key,
96 convert_security_scheme_ref(
97 security_scheme_ref,
98 ),
99 )
100 })
101 .collect()
102 });
103
104 Some(core::Components {
105 schemas,
106 responses,
107 parameters,
108 examples,
109 request_bodies,
110 headers,
111 security_schemes,
112 links,
113 })
114 } else {
115 None
116 };
117
118 let external_docs = spec.external_docs.map(convert_external_doc);
119
120 let tags = spec
121 .tags
122 .map(|tags| tags.into_iter().map(convert_tag).collect());
123
124 let info = spec.info.map(convert_info);
125
126 let servers = spec
127 .servers
128 .map(|servers| servers.into_iter().map(convert_server).collect());
129
130 core::HttpSchema {
131 version: spec.openapi,
132
133 schema_source: OpenApi303::id().to_owned(),
134 schema_source_version: VERSION.to_owned(),
135 schema_version: core::HttpSchema::schema_version().to_owned(),
136
137 info,
138 servers,
139 paths,
140 components,
141 tags,
142 external_docs,
143 }
144 }
145}
146
147fn convert_paths(
148 paths: IndexMap<String, MayBeRef303<Path>>,
149 context: &ConvertContext,
150) -> IndexMap<String, core::MayBeRef<core::Path>> {
151 paths
152 .into_iter()
153 .map(|(key, path_ref)| (key, convert_path_ref(path_ref, context)))
154 .collect()
155}
156
157fn convert_path_ref(
158 path_ref: MayBeRef303<Path>,
159 context: &ConvertContext,
160) -> core::MayBeRef<core::Path> {
161 match path_ref {
162 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
163 reference: value.reference,
164 }),
165 MayBeRef303::Value(value) => {
166 core::MayBeRef::Value(convert_path(value, context))
167 }
168 }
169}
170
171fn convert_path(path: Path, context: &ConvertContext) -> core::Path {
172 let parameters = &path.parameters;
173
174 let servers = path
175 .servers
176 .map(|servers| servers.into_iter().map(convert_server).collect());
177
178 core::Path {
179 get: path
180 .get
181 .map(|op| convert_operation(op, parameters, context)),
182 put: path
183 .put
184 .map(|op| convert_operation(op, parameters, context)),
185 post: path
186 .post
187 .map(|op| convert_operation(op, parameters, context)),
188 delete: path
189 .delete
190 .map(|op| convert_operation(op, parameters, context)),
191 options: path
192 .options
193 .map(|op| convert_operation(op, parameters, context)),
194 head: path
195 .head
196 .map(|op| convert_operation(op, parameters, context)),
197 patch: path
198 .patch
199 .map(|op| convert_operation(op, parameters, context)),
200 trace: path
201 .trace
202 .map(|op| convert_operation(op, parameters, context)),
203 servers,
204 summary: path.summary,
205 description: path.description,
206 }
207}
208
209fn merge_parameters(
210 context: &ConvertContext,
211 parameters_refs: Option<Vec<MayBeRef303<Parameter>>>,
212 path_parameters_refs: &Option<Vec<MayBeRef303<Parameter>>>,
213) -> Vec<MayBeRef303<Parameter>> {
214 let mut parameters: Vec<MayBeRef303<Parameter>> = Vec::new();
215
216 if let Some(parameters_refs) = parameters_refs {
217 parameters.extend(parameters_refs);
218 }
219
220 if let Some(parameters_refs) = path_parameters_refs {
221 parameters.extend(parameters_refs.clone());
222 }
223
224 let mut visited = Vec::new();
225
226 let mut result = Vec::with_capacity(parameters.len());
227
228 for may_be_parameter in parameters {
229 let key = match &may_be_parameter {
230 MayBeRef303::Ref(value) => {
231 if let Some(parameter) =
232 deref_parameter(context.components, value.reference())
233 {
234 (parameter.name.clone(), parameter.r#in.clone())
235 } else {
236 continue;
238 }
239 }
240 MayBeRef303::Value(value) => {
241 (value.name.clone(), value.r#in.clone())
242 }
243 };
244
245 if visited.contains(&key) {
246 continue;
247 }
248
249 result.push(may_be_parameter);
250
251 visited.push(key);
252 }
253
254 result
255}
256
257fn convert_operation(
258 operation: Operation,
259 path_parameters: &Option<Vec<MayBeRef303<Parameter>>>,
260 context: &ConvertContext,
261) -> core::Operation {
262 let merged_parameters = merge_parameters(
263 context, operation.parameters,
265 path_parameters,
266 );
267
268 let parameters = merged_parameters
269 .into_iter()
270 .map(convert_parameter_ref)
271 .collect();
272
273 let request_body = operation.request_body.map(convert_request_body_ref);
274
275 let responses = operation.responses.map(|responses| {
276 responses
277 .into_iter()
278 .map(|(code, response_ref)| {
279 (code, convert_response_ref(response_ref))
280 })
281 .collect()
282 });
283
284 let external_docs = operation.external_docs.map(convert_external_doc);
285
286 let servers = operation
287 .servers
288 .map(|servers| servers.into_iter().map(convert_server).collect());
289
290 core::Operation {
291 tags: operation.tags,
292 summary: operation.summary,
293 description: operation.description,
294 external_docs,
295 operation_id: operation.operation_id,
296 responses,
297 request_body,
298 servers,
299 parameters: Some(parameters),
300 security: operation.security,
301 deprecated: operation.deprecated,
302 }
303}
304
305fn convert_request_body_ref(
306 request_body_ref: MayBeRef303<RequestBody>,
307) -> core::MayBeRef<core::RequestBody> {
308 match request_body_ref {
309 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
310 reference: value.reference,
311 }),
312 MayBeRef303::Value(value) => {
313 core::MayBeRef::Value(convert_request_body(value))
314 }
315 }
316}
317
318fn convert_request_body(request_body: RequestBody) -> core::RequestBody {
319 core::RequestBody {
320 description: request_body.description,
321 required: request_body.required,
322 content: request_body.content.map(convert_media_types),
323 }
324}
325
326fn convert_media_types(
327 media_types: IndexMap<String, MediaType>,
328) -> IndexMap<String, core::MediaType> {
329 media_types
330 .into_iter()
331 .map(|(content_type, media_type)| {
332 (content_type, convert_media_type(media_type))
333 })
334 .collect()
335}
336
337fn convert_media_type(media_type: MediaType) -> core::MediaType {
338 core::MediaType {
365 schema: media_type.schema.map(convert_schema_ref),
366 examples: media_type.examples.map(convert_examples),
367 encoding: media_type.encoding.map(convert_encodings),
368 }
369}
370
371fn convert_encodings(
372 encodings: IndexMap<String, Encoding>,
373) -> IndexMap<String, core::Encoding> {
374 encodings
375 .into_iter()
376 .map(|(key, encoding)| (key, convert_encoding(encoding)))
377 .collect()
378}
379
380fn convert_encoding(encoding: Encoding) -> core::Encoding {
381 core::Encoding {
382 style: encoding.style,
383 explode: encoding.explode,
384 headers: encoding.headers.map(convert_headers),
385 content_type: encoding.content_type,
386 allow_reserved: encoding.allow_reserved,
387 }
388}
389
390fn convert_response_ref(
391 response_ref: MayBeRef303<Response>,
392) -> core::MayBeRef<core::Response> {
393 match response_ref {
394 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
395 reference: value.reference,
396 }),
397 MayBeRef303::Value(response) => {
398 core::MayBeRef::Value(convert_response(response))
399 }
400 }
401}
402
403fn convert_response(response: Response) -> core::Response {
404 let links = response.links.map(|links| {
405 links
406 .into_iter()
407 .map(|(key, link_ref)| (key, convert_link_ref(link_ref)))
408 .collect()
409 });
410
411 core::Response {
412 links,
413 description: response.description,
414 headers: response.headers.map(convert_headers),
415 content: response.content.map(convert_media_types),
416 }
417}
418
419fn convert_headers(
420 headers: IndexMap<String, MayBeRef303<Header>>,
421) -> IndexMap<String, core::MayBeRef<core::Header>> {
422 headers
423 .into_iter()
424 .map(|(key, header_ref)| (key, convert_header_ref(header_ref)))
425 .collect()
426}
427
428fn convert_header_ref(
429 header_ref: MayBeRef303<Header>,
430) -> core::MayBeRef<core::Header> {
431 match header_ref {
432 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
433 reference: value.reference,
434 }),
435 MayBeRef303::Value(value) => {
436 core::MayBeRef::Value(convert_header(value))
437 }
438 }
439}
440
441fn convert_header(header: Header) -> core::Header {
442 core::Header {
443 description: header.description,
444 required: header.required,
445 deprecated: header.deprecated,
446 allow_empty_value: header.allow_empty_value,
447 style: header.style,
448 explode: header.explode,
449 allow_reserved: header.allow_reserved,
450 schema: header.schema.map(convert_schema_ref),
451 examples: header.examples.map(convert_example_values),
452 content: header.content.map(convert_media_types),
453 custom_fields: header.custom_fields,
454 }
455}
456
457fn convert_parameter_ref(
458 parameter_ref: MayBeRef303<Parameter>,
459) -> core::MayBeRef<core::Parameter> {
460 match parameter_ref {
461 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
462 reference: value.reference,
463 }),
464 MayBeRef303::Value(parameter) => {
465 core::MayBeRef::Value(convert_parameter(parameter))
466 }
467 }
468}
469
470fn convert_parameter(parameter: Parameter) -> core::Parameter {
471 let schema = parameter.schema.map(convert_schema_ref);
472 core::Parameter {
473 name: parameter.name,
474 r#in: parameter.r#in,
475 description: parameter.description,
476 required: parameter.required,
477 deprecated: parameter.deprecated,
478 allow_empty_value: parameter.allow_empty_value,
479 style: parameter.style,
480 explode: parameter.explode,
481 allow_reserved: parameter.allow_reserved,
482 schema,
483 examples: parameter.examples.map(convert_example_values),
484 content: parameter.content.map(convert_media_types),
485 custom_fields: parameter.custom_fields,
486 }
487}
488
489fn convert_schema_ref(
490 schema_ref: MayBeRef303<Schema>,
491) -> core::MayBeRef<core::Schema> {
492 match schema_ref {
493 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
494 reference: value.reference,
495 }),
496 MayBeRef303::Value(value) => {
497 core::MayBeRef::Value(convert_schema(value))
498 }
499 }
500}
501
502fn convert_schema(schema: Schema) -> core::Schema {
503 let r#type = if let Some(nullable) = schema.nullable {
504 if let Some(type_) = schema.r#type {
505 match type_ {
506 Either::Left(single) => {
507 if nullable {
508 Some(Either::Right(Box::new(vec![
509 single,
510 "null".to_string(),
511 ])))
512 } else {
513 Some(Either::Left(single))
514 }
515 }
516 Either::Right(multiple) => {
517 let mut values = multiple;
518
519 if nullable {
520 values.push("null".to_string());
521 }
522
523 Some(Either::Right(values))
524 }
525 }
526 } else {
527 Some(Either::Right(Box::new(vec![
528 "object".to_string(),
529 "null".to_string(),
530 ])))
531 }
532 } else {
533 schema.r#type
534 };
535
536 let all_of = schema
537 .all_of
538 .map(|values| values.into_iter().map(convert_schema_ref).collect());
539
540 let one_of = schema
541 .one_of
542 .map(|values| values.into_iter().map(convert_schema_ref).collect());
543
544 let any_of = schema
545 .any_of
546 .map(|values| values.into_iter().map(convert_schema_ref).collect());
547
548 let not = schema
549 .not
550 .map(|values| values.into_iter().map(convert_schema_ref).collect());
551
552 let items = schema.items.map(convert_schema_ref);
553
554 let properties = schema.properties.map(|properties| {
555 properties
556 .into_iter()
557 .map(|(key, property_ref)| (key, convert_schema_ref(property_ref)))
558 .collect()
559 });
560
561 let additional_properties =
562 schema.additional_properties.map(|additional_properties| {
563 match additional_properties {
564 Either::Left(value) => Either::Left(value),
565 Either::Right(schema_ref) => {
566 Either::Right(Box::new(convert_schema_ref(*schema_ref)))
567 }
568 }
569 });
570
571 let discriminator =
572 schema
573 .discriminator
574 .map(|discriminator| core::Discriminator {
575 property_name: discriminator.property_name,
576 mapping: discriminator.mapping,
577 });
578
579 let xml = schema.xml.map(convert_xml);
580
581 let external_docs = schema.external_docs.map(convert_external_doc);
582
583 core::Schema {
584 title: schema.title,
585 multiple_of: schema.multiple_of,
586 maximum: schema.maximum,
587 exclusive_maximum: schema.exclusive_maximum,
588 minimum: schema.minimum,
589 exclusive_minimum: schema.exclusive_minimum,
590 max_length: schema.max_length,
591 min_length: schema.min_length,
592 pattern: schema.pattern,
593 max_items: schema.max_items,
594 min_items: schema.min_items,
595 unique_items: schema.unique_items,
596 max_properties: schema.max_properties,
597 min_properties: schema.min_properties,
598 required: schema.required,
599 r#enum: schema.r#enum,
600 r#type,
601 all_of,
602 one_of,
603 any_of,
604 not,
605 items: Box::new(items),
606 properties,
607 additional_properties,
608 description: schema.description,
609 format: schema.format,
610 default: schema.default,
611 discriminator,
612 read_only: schema.read_only,
613 write_only: schema.write_only,
614 xml,
615 external_docs,
616 example: schema.example,
617 deprecated: schema.deprecated,
618 custom_fields: schema.custom_fields,
619 }
620}
621
622fn convert_external_doc(external_doc: ExternalDoc) -> core::ExternalDoc {
623 core::ExternalDoc {
624 url: external_doc.url,
625 description: external_doc.description,
626 }
627}
628
629fn convert_xml(external_docs: Xml) -> core::Xml {
630 core::Xml {
631 name: external_docs.name,
632 namespace: external_docs.namespace,
633 prefix: external_docs.prefix,
634 attribute: external_docs.attribute,
635 wrapped: external_docs.wrapped,
636 }
637}
638
639fn convert_link_ref(
640 link_ref: MayBeRef303<Link>,
641) -> core::MayBeRef<core::Link> {
642 match link_ref {
643 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
644 reference: value.reference,
645 }),
646 MayBeRef303::Value(value) => {
647 core::MayBeRef::Value(convert_link(value))
648 }
649 }
650}
651
652fn convert_link(link: Link) -> core::Link {
653 core::Link {
654 operation_ref: link.operation_ref,
655 operation_id: link.operation_id,
656 parameters: link.parameters,
657 request_body: link.request_body,
658 description: link.description,
659 server: None,
660 }
661}
662
663fn convert_examples(
664 examples: IndexMap<String, MayBeRef303<Example>>,
665) -> IndexMap<String, core::MayBeRef<core::Example>> {
666 examples
667 .into_iter()
668 .map(|(key, example_ref)| (key, convert_example_ref(example_ref)))
669 .collect()
670}
671
672fn convert_example_values(
673 examples: IndexMap<String, MayBeRef303<Value>>,
674) -> IndexMap<String, core::MayBeRef<Value>> {
675 examples
676 .into_iter()
677 .map(|(key, example)| {
678 let example_ref = match example {
679 MayBeRef303::Ref(value) => {
680 core::MayBeRef::Ref(core::HttpSchemaRef {
681 reference: value.reference,
682 })
683 }
684 MayBeRef303::Value(value) => core::MayBeRef::Value(value),
685 };
686 (key, example_ref)
687 })
688 .collect()
689}
690
691fn convert_example_ref(
692 example_ref: MayBeRef303<Example>,
693) -> core::MayBeRef<core::Example> {
694 match example_ref {
695 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
696 reference: value.reference,
697 }),
698 MayBeRef303::Value(value) => {
699 core::MayBeRef::Value(convert_example(value))
700 }
701 }
702}
703
704fn convert_example(example: Example) -> core::Example {
705 core::Example {
706 summary: example.summary,
707 description: example.description,
708 value: example.value,
709 external_value: example.external_value,
710 }
711}
712
713fn convert_security_scheme_ref(
714 security_scheme_ref: MayBeRef303<SecurityScheme>,
715) -> core::MayBeRef<core::SecurityScheme> {
716 match security_scheme_ref {
717 MayBeRef303::Ref(value) => core::MayBeRef::Ref(core::HttpSchemaRef {
718 reference: value.reference,
719 }),
720 MayBeRef303::Value(value) => {
721 core::MayBeRef::Value(convert_security_scheme(value))
722 }
723 }
724}
725
726fn convert_security_scheme(
727 security_scheme: SecurityScheme,
728) -> core::SecurityScheme {
729 core::SecurityScheme {
730 r#type: security_scheme.r#type,
731 description: security_scheme.description,
732 name: security_scheme.name,
733 r#in: security_scheme.r#in,
734 scheme: security_scheme.scheme,
735 bearer_format: security_scheme.bearer_format,
736 flows: security_scheme.flows.map(convert_oauth_flows),
737 open_id_connect_url: security_scheme.open_id_connect_url,
738 }
739}
740
741fn convert_oauth_flows(oauth_flows: OAuthFlows) -> core::OAuthFlows {
742 core::OAuthFlows {
743 implicit: oauth_flows.implicit.map(convert_oauth_flow),
744 password: oauth_flows.password.map(convert_oauth_flow),
745 client_credentials: oauth_flows
746 .client_credentials
747 .map(convert_oauth_flow),
748 authorization_code: oauth_flows
749 .authorization_code
750 .map(convert_oauth_flow),
751 }
752}
753
754fn convert_oauth_flow(oauth_flow: OAuthFlow) -> core::OAuthFlow {
755 core::OAuthFlow {
756 authorization_url: oauth_flow.authorization_url,
757 token_url: oauth_flow.token_url,
758 refresh_url: oauth_flow.refresh_url,
759 scopes: oauth_flow.scopes,
760 }
761}
762
763fn convert_tag(tag: Tag) -> core::Tag {
764 core::Tag {
765 name: tag.name,
766 description: tag.description,
767 external_doc: tag.external_doc.map(convert_external_doc),
768 }
769}
770
771fn convert_info(info: Info) -> core::Info {
772 core::Info {
773 title: info.title,
774 description: info.description,
775 terms_of_service: info.terms_of_service,
776 contact: info.contact.map(convert_contact),
777 license: info.license.map(convert_license),
778 version: info.version,
779 }
780}
781
782fn convert_contact(contact: Contact) -> core::Contact {
783 core::Contact {
784 name: contact.name,
785 url: contact.url,
786 email: contact.email,
787 }
788}
789
790fn convert_license(license: License) -> core::License {
791 core::License {
792 name: license.name,
793 url: license.url,
794 }
795}
796
797fn convert_server(server: Server) -> core::Server {
798 core::Server {
799 url: server.url,
800 description: server.description,
801 variables: server.variables.map(|variables| {
802 variables
803 .into_iter()
804 .map(|(key, variable)| {
805 (key, convert_server_variable(variable))
806 })
807 .collect()
808 }),
809 }
810}
811
812fn convert_server_variable(
813 server_variable: ServerVariable,
814) -> core::ServerVariable {
815 core::ServerVariable {
816 r#enum: server_variable.r#enum,
817 default: server_variable.default,
818 description: server_variable.description,
819 }
820}