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}