use axum::{routing::MethodRouter, Json};
use serde_json::{json, Map, Value};
#[derive(Debug, Clone)]
pub struct RouteSpec {
pub path: String,
pub method: String,
pub summary: String,
pub operation_id: String,
}
#[derive(Debug, Clone)]
pub struct OpenApiSpec {
pub service_name: String,
pub version: String,
pub routes: Vec<RouteSpec>,
}
impl OpenApiSpec {
pub fn new(service_name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
service_name: service_name.into(),
version: version.into(),
routes: Vec::new(),
}
}
pub fn route(
mut self,
path: impl Into<String>,
method: impl Into<String>,
summary: impl Into<String>,
operation_id: impl Into<String>,
) -> Self {
self.routes.push(RouteSpec {
path: path.into(),
method: method.into(),
summary: summary.into(),
operation_id: operation_id.into(),
});
self
}
pub fn build_json(&self) -> Value {
let mut paths: Map<String, Value> = Map::new();
for r in &self.routes {
let method_key = r.method.to_lowercase();
let entry = paths.entry(r.path.clone()).or_insert_with(|| json!({}));
if let Value::Object(ref mut map) = entry {
map.insert(
method_key,
json!({
"summary": r.summary,
"operationId": r.operation_id,
"responses": { "200": { "description": "OK" } }
}),
);
}
}
json!({
"openapi": "3.1.0",
"info": {
"title": self.service_name,
"version": self.version,
},
"paths": paths,
})
}
pub fn build_handler(self) -> MethodRouter {
let json_val = self.build_json();
axum::routing::get(move || {
let v = json_val.clone();
async move { Json(v) }
})
}
}