1use once_cell::sync::OnceCell;
13use parking_lot::RwLock;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ModuleInfo {
20 pub name: String,
22 pub description: String,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ColumnInfo {
29 pub property_name: String,
31 pub r#type: String,
33 pub length: Option<u32>,
35 pub comment: Option<String>,
37 pub nullable: bool,
39 pub default_value: Option<String>,
41 pub dict: Option<serde_json::Value>,
43 pub source: String,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, Default)]
51pub struct QueryOpInfo {
52 pub key_word_like_fields: Vec<String>,
54 pub field_eq: Vec<String>,
56 pub field_like: Vec<String>,
58 pub order_by: Vec<(String, bool)>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct RouteInfo {
65 pub method: String,
67 pub path: String,
69 pub summary: Option<String>,
71 pub tag: Option<String>,
73 pub ignore_token: bool,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ControllerEps {
80 pub module: String,
82 pub prefix: String,
84 pub entity_name: Option<String>,
86 pub r#type: Option<String>,
88 pub description: Option<String>,
90 pub api: Vec<RouteInfo>,
92 pub columns: Vec<ColumnInfo>,
94 pub page_query_op: Option<QueryOpInfo>,
96 pub page_columns: Vec<ColumnInfo>,
98}
99
100#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
102pub enum EpsScope {
103 Admin,
105 App,
107}
108
109#[derive(Default)]
113pub struct EpsRegistry {
114 admin: RwLock<HashMap<String, Vec<ControllerEps>>>,
115 app: RwLock<HashMap<String, Vec<ControllerEps>>>,
116 module: RwLock<HashMap<String, ModuleInfo>>,
117}
118
119impl EpsRegistry {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn register_module(&self, key: impl Into<String>, info: ModuleInfo) {
127 let mut modules = self.module.write();
128 modules.insert(key.into(), info);
129 }
130
131 pub fn register_controller(
133 &self,
134 scope: EpsScope,
135 module: impl Into<String>,
136 eps: ControllerEps,
137 ) {
138 let module_key = module.into();
139 let target = match scope {
140 EpsScope::Admin => &self.admin,
141 EpsScope::App => &self.app,
142 };
143 let mut map = target.write();
144 map.entry(module_key).or_default().push(eps);
145 }
146
147 pub fn admin_eps(&self) -> HashMap<String, Vec<ControllerEps>> {
149 self.admin.read().clone()
150 }
151
152 pub fn app_eps(&self) -> HashMap<String, Vec<ControllerEps>> {
154 self.app.read().clone()
155 }
156
157 pub fn modules(&self) -> HashMap<String, ModuleInfo> {
159 self.module.read().clone()
160 }
161}
162
163static GLOBAL_EPS_REGISTRY: OnceCell<EpsRegistry> = OnceCell::new();
164
165pub fn global_eps_registry() -> &'static EpsRegistry {
167 GLOBAL_EPS_REGISTRY.get_or_init(EpsRegistry::default)
168}
169
170#[cfg(feature = "web")]
180pub mod handler {
181 use super::*;
182 use salvo::prelude::*;
183 use serde_json::json;
184
185 #[handler]
187 pub async fn admin_eps(_req: &mut Request, res: &mut Response) {
188 let registry = global_eps_registry();
189 let data = json!({
190 "modules": registry.modules(),
191 "eps": registry.admin_eps(),
192 });
193 res.render(Json(data));
194 }
195
196 #[handler]
198 pub async fn app_eps(_req: &mut Request, res: &mut Response) {
199 let registry = global_eps_registry();
200 let data = json!({
201 "modules": registry.modules(),
202 "eps": registry.app_eps(),
203 });
204 res.render(Json(data));
205 }
206
207 pub fn eps_router(admin_path: &str, app_path: &str) -> salvo::Router {
215 Router::new()
216 .push(Router::with_path(admin_path).get(admin_eps))
217 .push(Router::with_path(app_path).get(app_eps))
218 }
219}