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