1use crate::context::ActionContext;
2use serde_json::Value;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Lifecycle {
6 Oneshot,
7 Persistent,
8 Both,
9}
10
11#[derive(Debug, Clone)]
12pub struct Param {
13 pub name: String,
14 pub param_type: ParamType,
15 pub required: bool,
16 pub description: Option<String>,
17 pub default: Option<Value>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ParamType {
22 String,
23 Int,
24 Bool,
25 Array,
26}
27
28impl Param {
29 pub fn string(name: &str) -> Self {
30 Self {
31 name: name.to_string(),
32 param_type: ParamType::String,
33 required: false,
34 description: None,
35 default: None,
36 }
37 }
38 pub fn int(name: &str) -> Self {
39 Self {
40 name: name.to_string(),
41 param_type: ParamType::Int,
42 required: false,
43 description: None,
44 default: None,
45 }
46 }
47 pub fn bool_val(name: &str) -> Self {
48 Self {
49 name: name.to_string(),
50 param_type: ParamType::Bool,
51 required: false,
52 description: None,
53 default: None,
54 }
55 }
56 pub fn array(name: &str) -> Self {
57 Self {
58 name: name.to_string(),
59 param_type: ParamType::Array,
60 required: false,
61 description: None,
62 default: None,
63 }
64 }
65 pub fn required(mut self) -> Self {
66 self.required = true;
67 self
68 }
69 pub fn desc(mut self, d: &str) -> Self {
70 self.description = Some(d.to_string());
71 self
72 }
73 pub fn default_val(mut self, v: Value) -> Self {
74 self.default = Some(v);
75 self
76 }
77}
78
79pub type HandlerFn = Box<dyn Fn(Value, &ActionContext) -> anyhow::Result<Value> + Send + Sync>;
80
81pub struct Action {
82 pub name: String,
83 pub description: Option<String>,
84 pub params: Vec<Param>,
85 pub handler: HandlerFn,
86}
87
88impl Action {
89 pub fn new(
90 name: &str,
91 handler: impl Fn(Value, &ActionContext) -> anyhow::Result<Value> + Send + Sync + 'static,
92 ) -> Self {
93 Self {
94 name: name.to_string(),
95 description: None,
96 params: vec![],
97 handler: Box::new(handler),
98 }
99 }
100 pub fn description(mut self, d: &str) -> Self {
101 self.description = Some(d.to_string());
102 self
103 }
104 pub fn param(mut self, p: Param) -> Self {
105 self.params.push(p);
106 self
107 }
108}
109
110pub struct Domain {
111 pub name: String,
112 pub description: Option<String>,
113 pub actions: Vec<Action>,
114}
115
116impl Domain {
117 pub fn new(name: &str) -> Self {
118 Self {
119 name: name.to_string(),
120 description: None,
121 actions: vec![],
122 }
123 }
124 pub fn description(mut self, d: &str) -> Self {
125 self.description = Some(d.to_string());
126 self
127 }
128 pub fn action(mut self, a: Action) -> Self {
129 self.actions.push(a);
130 self
131 }
132}
133
134pub struct App {
135 pub name: String,
136 pub version: String,
137 pub lifecycle: Lifecycle,
138 pub domains: Vec<Domain>,
139}
140
141impl App {
142 pub fn new() -> Self {
143 Self {
144 name: String::new(),
145 version: "1.0.0".to_string(),
146 lifecycle: Lifecycle::Both,
147 domains: vec![],
148 }
149 }
150
151 pub fn name(mut self, n: &str) -> Self {
152 self.name = n.to_string();
153 self
154 }
155 pub fn version(mut self, v: &str) -> Self {
156 self.version = v.to_string();
157 self
158 }
159 pub fn lifecycle(mut self, l: Lifecycle) -> Self {
160 self.lifecycle = l;
161 self
162 }
163 pub fn domain(mut self, d: Domain) -> Self {
164 self.domains.push(d);
165 self
166 }
167
168 pub fn run(self) {
169 let args: Vec<String> = std::env::args().collect();
170 if args.len() > 1 && args[1] == "--capabilities" {
171 self.print_capabilities();
172 return;
173 }
174 if args.len() > 1 && args[1] == "--serve" {
175 self.run_persistent();
176 return;
177 }
178 self.run_oneshot(&args[1..]);
179 }
180
181 pub(crate) fn print_capabilities(&self) {
182 let caps = self.build_capabilities_json();
183 println!("{}", serde_json::to_string_pretty(&caps).unwrap());
184 }
185
186 pub(crate) fn build_capabilities_json(&self) -> Value {
187 let domains: Vec<Value> = self
188 .domains
189 .iter()
190 .map(|d| {
191 let actions: Vec<Value> = d
192 .actions
193 .iter()
194 .map(|a| {
195 let params: serde_json::Map<String, Value> = a
196 .params
197 .iter()
198 .map(|p| {
199 let mut m = serde_json::Map::new();
200 m.insert(
201 "类型".to_string(),
202 match p.param_type {
203 ParamType::String => Value::String("字符串".to_string()),
204 ParamType::Int => Value::String("整数".to_string()),
205 ParamType::Bool => Value::String("布尔".to_string()),
206 ParamType::Array => Value::String("数组".to_string()),
207 },
208 );
209 if p.required {
210 m.insert("必填".to_string(), Value::Bool(true));
211 }
212 if let Some(desc) = &p.description {
213 m.insert("描述".to_string(), Value::String(desc.clone()));
214 }
215 if let Some(default) = &p.default {
216 m.insert("默认".to_string(), default.clone());
217 }
218 (p.name.clone(), Value::Object(m))
219 })
220 .collect();
221 let mut obj = serde_json::Map::new();
222 obj.insert("名称".to_string(), Value::String(a.name.clone()));
223 if let Some(desc) = &a.description {
224 obj.insert("描述".to_string(), Value::String(desc.clone()));
225 }
226 obj.insert("参数".to_string(), Value::Object(params));
227 Value::Object(obj)
228 })
229 .collect();
230 let mut obj = serde_json::Map::new();
231 obj.insert("名称".to_string(), Value::String(d.name.clone()));
232 if let Some(desc) = &d.description {
233 obj.insert("描述".to_string(), Value::String(desc.clone()));
234 }
235 obj.insert("动作".to_string(), Value::Array(actions));
236 Value::Object(obj)
237 })
238 .collect();
239 let mut result = serde_json::Map::new();
240 result.insert("能力域".to_string(), Value::Array(domains));
241 Value::Object(result)
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_param_builder() {
251 let p = Param::string("name")
252 .required()
253 .desc("your name")
254 .default_val(Value::String("world".to_string()));
255 assert_eq!(p.name, "name");
256 assert_eq!(p.param_type, ParamType::String);
257 assert!(p.required);
258 assert_eq!(p.description.as_deref(), Some("your name"));
259 assert_eq!(p.default, Some(Value::String("world".to_string())));
260 }
261
262 #[test]
263 fn test_param_types() {
264 let p1 = Param::int("count");
265 assert_eq!(p1.param_type, ParamType::Int);
266 assert!(!p1.required);
267
268 let p2 = Param::bool_val("flag");
269 assert_eq!(p2.param_type, ParamType::Bool);
270
271 let p3 = Param::array("items");
272 assert_eq!(p3.param_type, ParamType::Array);
273 }
274
275 #[test]
276 fn test_app_builder() {
277 let app = App::new()
278 .name("test-app")
279 .version("2.0.0")
280 .lifecycle(Lifecycle::Oneshot);
281 assert_eq!(app.name, "test-app");
282 assert_eq!(app.version, "2.0.0");
283 assert_eq!(app.lifecycle, Lifecycle::Oneshot);
284 }
285
286 #[test]
287 fn test_build_capabilities_json() {
288 let app = App::new()
289 .name("demo")
290 .domain(
291 Domain::new("文件操作")
292 .description("文件相关操作")
293 .action(
294 Action::new(
295 "读取文件",
296 |_, _| Ok(Value::String("ok".to_string())),
297 )
298 .description("读取文件内容")
299 .param(Param::string("路径").required().desc("文件路径"))
300 .param(Param::bool_val("verbose").default_val(Value::Bool(false))),
301 ),
302 );
303
304 let caps = app.build_capabilities_json();
305
306 let domains = caps.get("能力域").unwrap().as_array().unwrap();
307 assert_eq!(domains.len(), 1);
308
309 let domain = &domains[0];
310 assert_eq!(domain.get("名称").unwrap().as_str().unwrap(), "文件操作");
311 assert_eq!(
312 domain.get("描述").unwrap().as_str().unwrap(),
313 "文件相关操作"
314 );
315
316 let actions = domain.get("动作").unwrap().as_array().unwrap();
317 assert_eq!(actions.len(), 1);
318
319 let action = &actions[0];
320 assert_eq!(action.get("名称").unwrap().as_str().unwrap(), "读取文件");
321 assert_eq!(action.get("描述").unwrap().as_str().unwrap(), "读取文件内容");
322
323 let params = action.get("参数").unwrap().as_object().unwrap();
324 let path_param = params.get("路径").unwrap().as_object().unwrap();
325 assert_eq!(path_param.get("类型").unwrap().as_str().unwrap(), "字符串");
326 assert_eq!(path_param.get("必填").unwrap().as_bool().unwrap(), true);
327 assert_eq!(
328 path_param.get("描述").unwrap().as_str().unwrap(),
329 "文件路径"
330 );
331
332 let verbose_param = params.get("verbose").unwrap().as_object().unwrap();
333 assert_eq!(verbose_param.get("类型").unwrap().as_str().unwrap(), "布尔");
334 assert_eq!(verbose_param.get("默认").unwrap().as_bool().unwrap(), false);
335 assert!(verbose_param.get("必填").is_none());
336 }
337
338 #[test]
339 fn test_multiple_domains() {
340 let app = App::new()
341 .name("multi")
342 .domain(Domain::new("域A").action(Action::new("动作1", |_, _| Ok(Value::Null))))
343 .domain(Domain::new("域B").action(Action::new("动作2", |_, _| Ok(Value::Null))));
344
345 let caps = app.build_capabilities_json();
346 let domains = caps.get("能力域").unwrap().as_array().unwrap();
347 assert_eq!(domains.len(), 2);
348 }
349}