use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInfo {
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnInfo {
pub property_name: String,
pub r#type: String,
pub length: Option<u32>,
pub comment: Option<String>,
pub nullable: bool,
pub default_value: Option<String>,
pub dict: Option<serde_json::Value>,
pub source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueryOpInfo {
pub key_word_like_fields: Vec<String>,
pub field_eq: Vec<String>,
pub field_like: Vec<String>,
pub order_by: Vec<(String, bool)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteInfo {
pub method: String,
pub path: String,
pub summary: Option<String>,
pub tag: Option<String>,
pub ignore_token: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControllerEps {
pub module: String,
pub prefix: String,
pub entity_name: Option<String>,
pub r#type: Option<String>,
pub description: Option<String>,
pub api: Vec<RouteInfo>,
pub columns: Vec<ColumnInfo>,
pub page_query_op: Option<QueryOpInfo>,
pub page_columns: Vec<ColumnInfo>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum EpsScope {
Admin,
App,
}
#[derive(Default)]
pub struct EpsRegistry {
admin: RwLock<HashMap<String, Vec<ControllerEps>>>,
app: RwLock<HashMap<String, Vec<ControllerEps>>>,
module: RwLock<HashMap<String, ModuleInfo>>,
}
impl EpsRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_module(&self, key: impl Into<String>, info: ModuleInfo) {
let mut modules = self.module.write();
modules.insert(key.into(), info);
}
pub fn register_controller(
&self,
scope: EpsScope,
module: impl Into<String>,
eps: ControllerEps,
) {
let module_key = module.into();
let target = match scope {
EpsScope::Admin => &self.admin,
EpsScope::App => &self.app,
};
let mut map = target.write();
map.entry(module_key).or_default().push(eps);
}
pub fn admin_eps(&self) -> HashMap<String, Vec<ControllerEps>> {
self.admin.read().clone()
}
pub fn app_eps(&self) -> HashMap<String, Vec<ControllerEps>> {
self.app.read().clone()
}
pub fn modules(&self) -> HashMap<String, ModuleInfo> {
self.module.read().clone()
}
}
static GLOBAL_EPS_REGISTRY: OnceCell<EpsRegistry> = OnceCell::new();
pub fn global_eps_registry() -> &'static EpsRegistry {
GLOBAL_EPS_REGISTRY.get_or_init(EpsRegistry::default)
}
#[cfg(feature = "web")]
pub mod handler {
use super::*;
use salvo::prelude::*;
use serde_json::json;
#[handler]
pub async fn admin_eps(_req: &mut Request, res: &mut Response) {
let registry = global_eps_registry();
let data = json!({
"modules": registry.modules(),
"eps": registry.admin_eps(),
});
res.render(Json(data));
}
#[handler]
pub async fn app_eps(_req: &mut Request, res: &mut Response) {
let registry = global_eps_registry();
let data = json!({
"modules": registry.modules(),
"eps": registry.app_eps(),
});
res.render(Json(data));
}
pub fn eps_router(admin_path: &str, app_path: &str) -> salvo::Router {
Router::new()
.push(Router::with_path(admin_path).get(admin_eps))
.push(Router::with_path(app_path).get(app_eps))
}
}