use std::collections::BTreeMap;
use std::path::Path;
use vespera_core::{
openapi::{Info, OpenApi, OpenApiVersion, Server},
route::{HttpMethod, PathItem},
schema::Components,
};
use crate::metadata::CollectedMetadata;
use crate::parser::{build_operation_from_function, parse_struct_to_schema};
pub fn generate_openapi_doc_with_metadata(
_folder_path: &Path,
_folder_name: &str,
title: Option<String>,
version: Option<String>,
metadata: &CollectedMetadata,
) -> OpenApi {
let mut paths: BTreeMap<String, PathItem> = BTreeMap::new();
let mut schemas: BTreeMap<String, vespera_core::schema::Schema> = BTreeMap::new();
let mut known_schema_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
for struct_meta in &metadata.structs {
let content = match std::fs::read_to_string(&struct_meta.file_path) {
Ok(content) => content,
Err(e) => {
eprintln!(
"Warning: Failed to read file {}: {}",
struct_meta.file_path, e
);
continue;
}
};
let file_ast = match syn::parse_file(&content) {
Ok(ast) => ast,
Err(e) => {
eprintln!(
"Warning: Failed to parse file {}: {}",
struct_meta.file_path, e
);
continue;
}
};
for item in file_ast.items {
if let syn::Item::Struct(struct_item) = item
&& struct_item.ident == struct_meta.name
{
let schema = parse_struct_to_schema(&struct_item, &known_schema_names);
let schema_name = struct_meta.name.clone();
schemas.insert(schema_name.clone(), schema);
known_schema_names.insert(schema_name.clone(), schema_name);
break;
}
}
}
for route_meta in &metadata.routes {
let content = match std::fs::read_to_string(&route_meta.file_path) {
Ok(content) => content,
Err(e) => {
eprintln!(
"Warning: Failed to read file {}: {}",
route_meta.file_path, e
);
continue;
}
};
let file_ast = match syn::parse_file(&content) {
Ok(ast) => ast,
Err(e) => {
eprintln!(
"Warning: Failed to parse file {}: {}",
route_meta.file_path, e
);
continue;
}
};
for item in file_ast.items {
if let syn::Item::Fn(fn_item) = item
&& fn_item.sig.ident == route_meta.function_name
{
let method = HttpMethod::from(route_meta.method.as_str());
let operation = build_operation_from_function(
&fn_item.sig,
&route_meta.path,
&known_schema_names,
route_meta.error_status.as_deref(),
);
let path_item = paths
.entry(route_meta.path.clone())
.or_insert_with(|| PathItem {
get: None,
post: None,
put: None,
patch: None,
delete: None,
head: None,
options: None,
trace: None,
parameters: None,
summary: None,
description: None,
});
path_item.set_operation(method, operation);
break;
}
}
}
OpenApi {
openapi: OpenApiVersion::V3_1_0,
info: Info {
title: title.unwrap_or_else(|| "API".to_string()),
version: version.unwrap_or_else(|| "1.0.0".to_string()),
description: None,
terms_of_service: None,
contact: None,
license: None,
summary: None,
},
servers: Some(vec![Server {
url: "http://localhost:3000".to_string(),
description: None,
variables: None,
}]),
paths,
components: Some(Components {
schemas: if schemas.is_empty() {
None
} else {
Some(schemas)
},
responses: None,
parameters: None,
examples: None,
request_bodies: None,
headers: None,
security_schemes: None,
}),
security: None,
tags: None,
external_docs: None,
}
}