openapi_model_generator/
parser.rs1use crate::{models::{Model, Field, RequestModel, ResponseModel}, Result};
2use openapiv3::{OpenAPI, Schema, ReferenceOr, SchemaKind, Type, VariantOrUnknownOrEmpty, StringFormat};
3
4pub fn parse_openapi(openapi: &OpenAPI) -> Result<(Vec<Model>, Vec<RequestModel>, Vec<ResponseModel>)> {
5 let mut models = Vec::new();
6 let mut requests = Vec::new();
7 let mut responses = Vec::new();
8
9 if let Some(components) = &openapi.components {
11 for (name, schema) in &components.schemas {
12 if let Some(model) = parse_schema(name, schema)? {
13 models.push(model);
14 }
15 }
16 }
17
18 for (_path, path_item) in openapi.paths.iter() {
20 let path_item = match path_item {
21 ReferenceOr::Item(item) => item,
22 ReferenceOr::Reference { .. } => continue,
23 };
24
25 if let Some(op) = &path_item.get {
26 process_operation(op, &mut requests, &mut responses)?;
27 }
28 if let Some(op) = &path_item.post {
29 process_operation(op, &mut requests, &mut responses)?;
30 }
31 if let Some(op) = &path_item.put {
32 process_operation(op, &mut requests, &mut responses)?;
33 }
34 if let Some(op) = &path_item.delete {
35 process_operation(op, &mut requests, &mut responses)?;
36 }
37 if let Some(op) = &path_item.patch {
38 process_operation(op, &mut requests, &mut responses)?;
39 }
40 }
41
42 Ok((models, requests, responses))
43}
44
45fn process_operation(
46 operation: &openapiv3::Operation,
47 requests: &mut Vec<RequestModel>,
48 responses: &mut Vec<ResponseModel>,
49) -> Result<()> {
50 if let Some(request_body_ref) = &operation.request_body {
52 if let ReferenceOr::Item(request_body) = request_body_ref {
53 for (content_type, media_type) in &request_body.content {
54 if let Some(schema) = &media_type.schema {
55 let request = RequestModel {
56 name: format!("{}Request", operation.operation_id.as_deref().unwrap_or("Unknown")),
57 content_type: content_type.clone(),
58 schema: extract_type(schema)?,
59 is_required: request_body.required,
60 };
61 requests.push(request);
62 }
63 }
64 }
65 }
66
67 for (status, response_ref) in operation.responses.responses.iter() {
69 if let ReferenceOr::Item(response) = response_ref {
70 for (content_type, media_type) in &response.content {
71 if let Some(schema) = &media_type.schema {
72 let response = ResponseModel {
73 name: format!("{}Response", operation.operation_id.as_deref().unwrap_or("Unknown")),
74 status_code: status.to_string(),
75 content_type: content_type.clone(),
76 schema: extract_type(schema)?,
77 description: Some(response.description.clone()),
78 };
79 responses.push(response);
80 }
81 }
82 }
83 }
84
85 Ok(())
86}
87
88fn parse_schema(name: &str, schema: &ReferenceOr<Schema>) -> Result<Option<Model>> {
89 match schema {
90 ReferenceOr::Reference { .. } => Ok(None),
91 ReferenceOr::Item(schema) => {
92 if let SchemaKind::Type(Type::Object(obj)) = &schema.schema_kind {
93 let mut fields = Vec::new();
94 for (field_name, field_schema) in &obj.properties {
95 let field_type = match field_schema {
96 ReferenceOr::Item(boxed_schema) => {
97 extract_type(&ReferenceOr::Item((**boxed_schema).clone()))?
98 },
99 ReferenceOr::Reference { reference } => {
100 extract_type(&ReferenceOr::Reference { reference: reference.clone() })?
101 }
102 };
103 let is_required = obj.required.contains(field_name);
104 fields.push(Field {
105 name: field_name.clone(),
106 field_type,
107 is_required,
108 });
109 }
110 Ok(Some(Model {
111 name: name.to_string(),
112 fields,
113 }))
114 } else {
115 Ok(None)
116 }
117 }
118 }
119}
120
121fn extract_type(schema: &ReferenceOr<Schema>) -> Result<String> {
122 match schema {
123 ReferenceOr::Reference { reference } => {
124 let type_name = reference.split('/').last().unwrap_or("Unknown");
125 Ok(type_name.to_string())
126 }
127 ReferenceOr::Item(schema) => {
128 match &schema.schema_kind {
129 SchemaKind::Type(Type::String(string_type)) => {
130 match &string_type.format {
131 VariantOrUnknownOrEmpty::Item(fmt) => match fmt {
132 StringFormat::DateTime => Ok("DateTime<Utc>".to_string()),
133 StringFormat::Date => Ok("NaiveDate".to_string()),
134 _ => Ok("String".to_string()),
135 },
136 _ => Ok("String".to_string()),
137 }
138 }
139 SchemaKind::Type(Type::Integer(_)) => Ok("i64".to_string()),
140 SchemaKind::Type(Type::Number(_)) => Ok("f64".to_string()),
141 SchemaKind::Type(Type::Boolean {}) => Ok("bool".to_string()),
142 SchemaKind::Type(Type::Array(arr)) => {
143 if let Some(items) = &arr.items {
144 let items_ref: &ReferenceOr<Box<Schema>> = &*items;
145 let inner_type = match items_ref {
146 ReferenceOr::Item(boxed_schema) => extract_type(&ReferenceOr::Item((**boxed_schema).clone()))?,
147 ReferenceOr::Reference { reference } => extract_type(&ReferenceOr::Reference { reference: reference.clone() })?,
148 };
149 Ok(format!("Vec<{}>", inner_type))
150 } else {
151 Ok("Vec<serde_json::Value>".to_string())
152 }
153 }
154 SchemaKind::Type(Type::Object(obj)) => {
155 if obj.properties.is_empty() {
156 Ok("()".to_string())
157 } else {
158 Ok("serde_json::Value".to_string())
159 }
160 }
161 _ => Ok("serde_json::Value".to_string()),
162 }
163 }
164 }
165}