amalgam_parser/
openapi.rs1use crate::{Parser, ParserError};
4use amalgam_core::{
5 ir::{IRBuilder, IR},
6 types::{Field, Type},
7};
8use openapiv3::{OpenAPI, Schema, SchemaKind, Type as OpenAPIType};
9use std::collections::BTreeMap;
10
11pub struct OpenAPIParser;
12
13impl Parser for OpenAPIParser {
14 type Input = OpenAPI;
15
16 fn parse(&self, input: Self::Input) -> Result<IR, ParserError> {
17 let mut builder = IRBuilder::new().module("openapi");
18
19 if let Some(components) = input.components {
21 for (name, schema_ref) in components.schemas {
22 if let openapiv3::ReferenceOr::Item(schema) = schema_ref {
23 let ty = self.schema_to_type(&schema)?;
24 builder = builder.add_type(name, ty);
25 }
26 }
27 }
28
29 Ok(builder.build())
30 }
31}
32
33impl OpenAPIParser {
34 pub fn new() -> Self {
35 Self
36 }
37
38 #[allow(clippy::only_used_in_recursion)]
39 fn schema_to_type(&self, schema: &Schema) -> Result<Type, ParserError> {
40 match &schema.schema_kind {
41 SchemaKind::Type(OpenAPIType::String(_)) => Ok(Type::String),
42 SchemaKind::Type(OpenAPIType::Number(_)) => Ok(Type::Number),
43 SchemaKind::Type(OpenAPIType::Integer(_)) => Ok(Type::Integer),
44 SchemaKind::Type(OpenAPIType::Boolean(_)) => Ok(Type::Bool),
45 SchemaKind::Type(OpenAPIType::Array(array_type)) => {
46 let item_type = array_type
47 .items
48 .as_ref()
49 .and_then(|i| i.as_item())
50 .map(|s| self.schema_to_type(s))
51 .transpose()?
52 .unwrap_or(Type::Any);
53 Ok(Type::Array(Box::new(item_type)))
54 }
55 SchemaKind::Type(OpenAPIType::Object(object_type)) => {
56 let mut fields = BTreeMap::new();
57 for (field_name, field_schema_ref) in &object_type.properties {
58 if let openapiv3::ReferenceOr::Item(field_schema) = field_schema_ref {
59 let field_type = self.schema_to_type(field_schema)?;
60 let required = object_type.required.contains(field_name);
61 fields.insert(
62 field_name.clone(),
63 Field {
64 ty: field_type,
65 required,
66 description: field_schema.schema_data.description.clone(),
67 default: None,
68 },
69 );
70 }
71 }
72 Ok(Type::Record {
73 fields,
74 open: object_type.additional_properties.is_some(),
75 })
76 }
77 SchemaKind::OneOf { one_of } => {
78 let mut types = Vec::new();
79 for schema_ref in one_of {
80 if let openapiv3::ReferenceOr::Item(schema) = schema_ref {
81 types.push(self.schema_to_type(schema)?);
82 }
83 }
84 Ok(Type::Union(types))
85 }
86 SchemaKind::AllOf { all_of: _ } => {
87 Ok(Type::Any)
89 }
90 SchemaKind::AnyOf { any_of } => {
91 let mut types = Vec::new();
92 for schema_ref in any_of {
93 if let openapiv3::ReferenceOr::Item(schema) = schema_ref {
94 types.push(self.schema_to_type(schema)?);
95 }
96 }
97 Ok(Type::Union(types))
98 }
99 SchemaKind::Not { .. } => {
100 Err(ParserError::UnsupportedFeature("'not' schema".to_string()))
101 }
102 SchemaKind::Any(_) => Ok(Type::Any),
103 }
104 }
105}
106
107impl Default for OpenAPIParser {
108 fn default() -> Self {
109 Self::new()
110 }
111}