Skip to main content

fa_bridge_sdk/
types.rs

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}