1use crate::profile::TokenType;
9use serde_json::{json, Value};
10use std::collections::HashMap;
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum ArgsError {
15 #[error("Missing method argument")]
16 MissingMethod,
17
18 #[error("Invalid key-value pair: {0}")]
19 InvalidKeyValue(String),
20
21 #[error("Invalid JSON: {0}")]
22 InvalidJson(String),
23}
24
25pub type Result<T> = std::result::Result<T, ArgsError>;
26
27#[derive(Debug, Clone, PartialEq)]
29pub struct ApiCallArgs {
30 pub method: String,
32
33 pub params: HashMap<String, String>,
35
36 pub use_json: bool,
38
39 pub use_get: bool,
41
42 pub token_type: Option<TokenType>,
44
45 pub raw: bool,
47}
48
49impl ApiCallArgs {
50 pub fn parse(args: &[String]) -> Result<Self> {
52 if args.is_empty() {
53 return Err(ArgsError::MissingMethod);
54 }
55
56 let method = args[0].clone();
57 let mut params = HashMap::new();
58 let mut use_json = false;
59 let mut use_get = false;
60 let mut token_type = None;
61 let mut raw = false;
62
63 let mut i = 1;
64 while i < args.len() {
65 let arg = &args[i];
66 if arg == "--json" {
67 use_json = true;
68 } else if arg == "--get" {
69 use_get = true;
70 } else if arg == "--raw" {
71 raw = true;
72 } else if arg == "--token-type" {
73 i += 1;
75 if i < args.len() {
76 token_type = Some(
77 args[i]
78 .parse::<TokenType>()
79 .map_err(|e| ArgsError::InvalidJson(e.to_string()))?,
80 );
81 }
82 } else if arg.starts_with("--token-type=") {
83 if let Some(value) = arg.strip_prefix("--token-type=") {
85 token_type = Some(
86 value
87 .parse::<TokenType>()
88 .map_err(|e| ArgsError::InvalidJson(e.to_string()))?,
89 );
90 }
91 } else if arg.starts_with("--") {
92 } else {
94 if let Some((key, value)) = arg.split_once('=') {
96 params.insert(key.to_string(), value.to_string());
97 } else {
98 return Err(ArgsError::InvalidKeyValue(arg.clone()));
99 }
100 }
101 i += 1;
102 }
103
104 Ok(Self {
105 method,
106 params,
107 use_json,
108 use_get,
109 token_type,
110 raw,
111 })
112 }
113
114 pub fn to_json(&self) -> Value {
116 let mut map = serde_json::Map::new();
117 for (k, v) in &self.params {
118 map.insert(k.clone(), Value::String(v.clone()));
119 }
120 Value::Object(map)
121 }
122
123 pub fn to_form(&self) -> Vec<(String, String)> {
125 self.params
126 .iter()
127 .map(|(k, v)| (k.clone(), v.clone()))
128 .collect()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_parse_basic() {
138 let args = vec!["chat.postMessage".to_string()];
139 let result = ApiCallArgs::parse(&args).unwrap();
140
141 assert_eq!(result.method, "chat.postMessage");
142 assert!(result.params.is_empty());
143 assert!(!result.use_json);
144 assert!(!result.use_get);
145 assert_eq!(result.token_type, None);
146 }
147
148 #[test]
149 fn test_parse_with_params() {
150 let args = vec![
151 "chat.postMessage".to_string(),
152 "channel=C123456".to_string(),
153 "text=Hello World".to_string(),
154 ];
155 let result = ApiCallArgs::parse(&args).unwrap();
156
157 assert_eq!(result.method, "chat.postMessage");
158 assert_eq!(result.params.get("channel"), Some(&"C123456".to_string()));
159 assert_eq!(result.params.get("text"), Some(&"Hello World".to_string()));
160 }
161
162 #[test]
163 fn test_parse_with_json_flag() {
164 let args = vec![
165 "chat.postMessage".to_string(),
166 "--json".to_string(),
167 "channel=C123456".to_string(),
168 ];
169 let result = ApiCallArgs::parse(&args).unwrap();
170
171 assert_eq!(result.method, "chat.postMessage");
172 assert!(result.use_json);
173 assert!(!result.use_get);
174 }
175
176 #[test]
177 fn test_parse_with_get_flag() {
178 let args = vec![
179 "users.info".to_string(),
180 "--get".to_string(),
181 "user=U123456".to_string(),
182 ];
183 let result = ApiCallArgs::parse(&args).unwrap();
184
185 assert_eq!(result.method, "users.info");
186 assert!(!result.use_json);
187 assert!(result.use_get);
188 }
189
190 #[test]
191 fn test_parse_with_both_flags() {
192 let args = vec![
193 "chat.postMessage".to_string(),
194 "--json".to_string(),
195 "--get".to_string(),
196 "channel=C123456".to_string(),
197 ];
198 let result = ApiCallArgs::parse(&args).unwrap();
199
200 assert!(result.use_json);
201 assert!(result.use_get);
202 }
203
204 #[test]
205 fn test_parse_missing_method() {
206 let args: Vec<String> = vec![];
207 let result = ApiCallArgs::parse(&args);
208
209 assert!(result.is_err());
210 match result {
211 Err(ArgsError::MissingMethod) => {}
212 _ => panic!("Expected MissingMethod error"),
213 }
214 }
215
216 #[test]
217 fn test_parse_invalid_key_value() {
218 let args = vec!["chat.postMessage".to_string(), "invalid_arg".to_string()];
219 let result = ApiCallArgs::parse(&args);
220
221 assert!(result.is_err());
222 match result {
223 Err(ArgsError::InvalidKeyValue(arg)) => {
224 assert_eq!(arg, "invalid_arg");
225 }
226 _ => panic!("Expected InvalidKeyValue error"),
227 }
228 }
229
230 #[test]
231 fn test_to_json() {
232 let args = ApiCallArgs {
233 method: "chat.postMessage".to_string(),
234 params: [
235 ("channel".to_string(), "C123456".to_string()),
236 ("text".to_string(), "Hello".to_string()),
237 ]
238 .iter()
239 .cloned()
240 .collect(),
241 use_json: true,
242 use_get: false,
243 token_type: None,
244 raw: false,
245 };
246
247 let json = args.to_json();
248 assert_eq!(json["channel"], "C123456");
249 assert_eq!(json["text"], "Hello");
250 }
251
252 #[test]
253 fn test_to_form() {
254 let args = ApiCallArgs {
255 method: "chat.postMessage".to_string(),
256 params: [
257 ("channel".to_string(), "C123456".to_string()),
258 ("text".to_string(), "Hello".to_string()),
259 ]
260 .iter()
261 .cloned()
262 .collect(),
263 use_json: false,
264 use_get: false,
265 token_type: None,
266 raw: false,
267 };
268
269 let form = args.to_form();
270 assert_eq!(form.len(), 2);
271 assert!(form.contains(&("channel".to_string(), "C123456".to_string())));
272 assert!(form.contains(&("text".to_string(), "Hello".to_string())));
273 }
274
275 #[test]
276 fn test_parse_token_type_space_separated() {
277 let args = vec![
278 "chat.postMessage".to_string(),
279 "--token-type".to_string(),
280 "user".to_string(),
281 "channel=C123456".to_string(),
282 ];
283 let result = ApiCallArgs::parse(&args).unwrap();
284
285 assert_eq!(result.method, "chat.postMessage");
286 assert_eq!(result.token_type, Some(TokenType::User));
287 }
288
289 #[test]
290 fn test_parse_token_type_equals_format() {
291 let args = vec![
292 "chat.postMessage".to_string(),
293 "--token-type=bot".to_string(),
294 "channel=C123456".to_string(),
295 ];
296 let result = ApiCallArgs::parse(&args).unwrap();
297
298 assert_eq!(result.method, "chat.postMessage");
299 assert_eq!(result.token_type, Some(TokenType::Bot));
300 }
301
302 #[test]
303 fn test_parse_token_type_both_formats() {
304 let args1 = vec![
306 "users.info".to_string(),
307 "--token-type".to_string(),
308 "bot".to_string(),
309 ];
310 let result1 = ApiCallArgs::parse(&args1).unwrap();
311 assert_eq!(result1.token_type, Some(TokenType::Bot));
312
313 let args2 = vec!["users.info".to_string(), "--token-type=user".to_string()];
315 let result2 = ApiCallArgs::parse(&args2).unwrap();
316 assert_eq!(result2.token_type, Some(TokenType::User));
317 }
318}