Skip to main content

fa_bridge_sdk/
oneshot.rs

1use crate::context::{ActionContext, RunMode};
2use crate::types::App;
3
4pub fn parse_cli_args(args: &[String]) -> serde_json::Map<String, serde_json::Value> {
5    let mut params = serde_json::Map::new();
6    let mut i = 0;
7    while i < args.len() {
8        let arg = &args[i];
9        if arg.starts_with("--") {
10            let key = arg.trim_start_matches("--").to_string();
11            if i + 1 < args.len() {
12                let val = &args[i + 1];
13                if let Ok(n) = val.parse::<i64>() {
14                    params.insert(key, serde_json::Value::Number(n.into()));
15                } else if val == "true" {
16                    params.insert(key, serde_json::Value::Bool(true));
17                } else if val == "false" {
18                    params.insert(key, serde_json::Value::Bool(false));
19                } else {
20                    params.insert(key, serde_json::Value::String(val.clone()));
21                }
22                i += 2;
23            } else {
24                params.insert(key, serde_json::Value::Bool(true));
25                i += 1;
26            }
27        } else {
28            i += 1;
29        }
30    }
31    params
32}
33
34impl App {
35    pub fn run_oneshot(&self, args: &[String]) {
36        if args.is_empty() {
37            eprintln!("Usage: <binary> <action> [--param value ...]");
38            std::process::exit(1);
39        }
40
41        let action_name = &args[0];
42        let params = parse_cli_args(&args[1..]);
43        let params_value = serde_json::Value::Object(params);
44
45        for domain in &self.domains {
46            for action in &domain.actions {
47                if action.name == *action_name {
48                    let ctx = ActionContext {
49                        domain: domain.name.clone(),
50                        action: action.name.clone(),
51                        mode: RunMode::Oneshot,
52                    };
53                    match (action.handler)(params_value, &ctx) {
54                        Ok(result) => {
55                            println!("{}", serde_json::to_string_pretty(&result).unwrap());
56                            std::process::exit(0);
57                        }
58                        Err(e) => {
59                            let err_msg = serde_json::json!({
60                                "error": format!("{}", e)
61                            });
62                            eprintln!(
63                                "{}",
64                                serde_json::to_string_pretty(&err_msg).unwrap()
65                            );
66                            std::process::exit(1);
67                        }
68                    }
69                }
70            }
71        }
72
73        eprintln!("Unknown action: {}", action_name);
74        std::process::exit(1);
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_parse_empty_args() {
84        let args: Vec<String> = vec![];
85        let params = parse_cli_args(&args);
86        assert!(params.is_empty());
87    }
88
89    #[test]
90    fn test_parse_string_param() {
91        let args = vec!["--name".to_string(), "hello".to_string()];
92        let params = parse_cli_args(&args);
93        assert_eq!(params.get("name").unwrap().as_str().unwrap(), "hello");
94    }
95
96    #[test]
97    fn test_parse_int_param() {
98        let args = vec!["--count".to_string(), "42".to_string()];
99        let params = parse_cli_args(&args);
100        assert_eq!(params.get("count").unwrap().as_i64().unwrap(), 42);
101    }
102
103    #[test]
104    fn test_parse_bool_true_param() {
105        let args = vec!["--verbose".to_string(), "true".to_string()];
106        let params = parse_cli_args(&args);
107        assert_eq!(params.get("verbose").unwrap().as_bool().unwrap(), true);
108    }
109
110    #[test]
111    fn test_parse_bool_false_param() {
112        let args = vec!["--verbose".to_string(), "false".to_string()];
113        let params = parse_cli_args(&args);
114        assert_eq!(params.get("verbose").unwrap().as_bool().unwrap(), false);
115    }
116
117    #[test]
118    fn test_parse_flag_without_value() {
119        let args = vec!["--debug".to_string()];
120        let params = parse_cli_args(&args);
121        assert_eq!(params.get("debug").unwrap().as_bool().unwrap(), true);
122    }
123
124    #[test]
125    fn test_parse_multiple_params() {
126        let args = vec![
127            "--name".to_string(),
128            "test".to_string(),
129            "--count".to_string(),
130            "10".to_string(),
131            "--verbose".to_string(),
132            "true".to_string(),
133        ];
134        let params = parse_cli_args(&args);
135        assert_eq!(params.get("name").unwrap().as_str().unwrap(), "test");
136        assert_eq!(params.get("count").unwrap().as_i64().unwrap(), 10);
137        assert_eq!(params.get("verbose").unwrap().as_bool().unwrap(), true);
138        assert_eq!(params.len(), 3);
139    }
140
141    #[test]
142    fn test_parse_non_flag_args_ignored() {
143        let args = vec![
144            "action_name".to_string(),
145            "--key".to_string(),
146            "value".to_string(),
147        ];
148        let params = parse_cli_args(&args);
149        assert!(params.get("action_name").is_none());
150        assert_eq!(params.get("key").unwrap().as_str().unwrap(), "value");
151    }
152
153    #[test]
154    fn test_parse_negative_int() {
155        let args = vec!["--offset".to_string(), "-5".to_string()];
156        let params = parse_cli_args(&args);
157        assert_eq!(params.get("offset").unwrap().as_i64().unwrap(), -5);
158    }
159}