use super::generator::SchemaGenerator;
use super::registry::get_all_schemas;
use super::swagger::{RedocUI, SwaggerUI};
use crate::openapi::OpenApiSchema;
use reinhardt_http::{Request, Response, Result};
use std::sync::LazyLock;
static OPENAPI_SCHEMA: LazyLock<OpenApiSchema> = LazyLock::new(generate_openapi_schema);
static SWAGGER_UI: LazyLock<SwaggerUI> = LazyLock::new(|| SwaggerUI::new(OPENAPI_SCHEMA.clone()));
static REDOC_UI: LazyLock<RedocUI> = LazyLock::new(|| RedocUI::new(OPENAPI_SCHEMA.clone()));
pub fn generate_openapi_schema() -> OpenApiSchema {
let mut generator = SchemaGenerator::new()
.title("API Documentation")
.version("1.0.0")
.description("Auto-generated API documentation");
let registry = generator.registry();
for (name, schema) in get_all_schemas().iter() {
registry.register(*name, schema.clone());
}
generator = generator.add_function_based_endpoints();
generator
.generate()
.expect("Failed to generate OpenAPI schema")
}
pub async fn swagger_docs(_req: Request) -> Result<Response> {
let html = SWAGGER_UI.render_html().map_err(|e| {
reinhardt_core::exception::Error::Serialization(format!(
"Failed to render Swagger UI: {}",
e
))
})?;
Ok(Response::ok()
.with_header("Content-Type", "text/html; charset=utf-8")
.with_body(html))
}
pub async fn redoc_docs(_req: Request) -> Result<Response> {
let html = REDOC_UI.render_html().map_err(|e| {
reinhardt_core::exception::Error::Serialization(format!("Failed to render Redoc UI: {}", e))
})?;
Ok(Response::ok()
.with_header("Content-Type", "text/html; charset=utf-8")
.with_body(html))
}
pub async fn openapi_json(_req: Request) -> Result<Response> {
let json = serde_json::to_string_pretty(&*OPENAPI_SCHEMA).map_err(|e| {
reinhardt_core::exception::Error::Serialization(format!("JSON serialization error: {}", e))
})?;
Ok(Response::ok()
.with_header("Content-Type", "application/json; charset=utf-8")
.with_body(json))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_openapi_schema_generation() {
let schema = &*OPENAPI_SCHEMA;
assert_eq!(schema.info.title, "API Documentation");
assert_eq!(schema.info.version, "1.0.0");
}
#[tokio::test]
async fn test_swagger_docs_handler() {
let req = Request::builder().uri("/docs").build().unwrap();
let response = swagger_docs(req).await.unwrap();
let body_str = String::from_utf8(response.body.to_vec()).unwrap();
assert!(body_str.contains("swagger-ui"));
}
#[tokio::test]
async fn test_redoc_docs_handler() {
let req = Request::builder().uri("/docs-redoc").build().unwrap();
let response = redoc_docs(req).await.unwrap();
let body_str = String::from_utf8(response.body.to_vec()).unwrap();
assert!(body_str.contains("redoc"));
}
#[tokio::test]
async fn test_openapi_json_handler() {
let req = Request::builder().uri("/api/openapi.json").build().unwrap();
let response = openapi_json(req).await.unwrap();
let body_str = String::from_utf8(response.body.to_vec()).unwrap();
let _: serde_json::Value = serde_json::from_str(&body_str).unwrap();
let content_type = response
.headers
.get("content-type")
.and_then(|h| h.to_str().ok())
.unwrap_or("");
assert!(content_type.contains("application/json"));
}
}