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
62 let mut raw = if let Ok(output_mode) = std::env::var("SLACKRS_OUTPUT") {
65 output_mode.trim().to_lowercase() == "raw"
66 } else {
67 false
68 };
69
70 let mut i = 1;
71 while i < args.len() {
72 let arg = &args[i];
73 if arg == "--json" {
74 use_json = true;
75 } else if arg == "--get" {
76 use_get = true;
77 } else if arg == "--raw" {
78 raw = true;
80 } else if arg == "--profile" {
81 i += 1; } else if arg.starts_with("--profile=") {
84 } else if arg == "--token-type" {
87 i += 1;
89 if i < args.len() {
90 token_type = Some(
91 args[i]
92 .parse::<TokenType>()
93 .map_err(|e| ArgsError::InvalidJson(e.to_string()))?,
94 );
95 }
96 } else if arg.starts_with("--token-type=") {
97 if let Some(value) = arg.strip_prefix("--token-type=") {
99 token_type = Some(
100 value
101 .parse::<TokenType>()
102 .map_err(|e| ArgsError::InvalidJson(e.to_string()))?,
103 );
104 }
105 } else if arg.starts_with("--") {
106 } else {
108 if let Some((key, value)) = arg.split_once('=') {
110 params.insert(key.to_string(), value.to_string());
111 } else {
112 return Err(ArgsError::InvalidKeyValue(arg.clone()));
113 }
114 }
115 i += 1;
116 }
117
118 Ok(Self {
119 method,
120 params,
121 use_json,
122 use_get,
123 token_type,
124 raw,
125 })
126 }
127
128 pub fn to_json(&self) -> Value {
130 let mut map = serde_json::Map::new();
131 for (k, v) in &self.params {
132 map.insert(k.clone(), Value::String(v.clone()));
133 }
134 Value::Object(map)
135 }
136
137 pub fn to_form(&self) -> Vec<(String, String)> {
139 self.params
140 .iter()
141 .map(|(k, v)| (k.clone(), v.clone()))
142 .collect()
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_parse_basic() {
152 let args = vec!["chat.postMessage".to_string()];
153 let result = ApiCallArgs::parse(&args).unwrap();
154
155 assert_eq!(result.method, "chat.postMessage");
156 assert!(result.params.is_empty());
157 assert!(!result.use_json);
158 assert!(!result.use_get);
159 assert_eq!(result.token_type, None);
160 }
161
162 #[test]
163 fn test_parse_with_params() {
164 let args = vec![
165 "chat.postMessage".to_string(),
166 "channel=C123456".to_string(),
167 "text=Hello World".to_string(),
168 ];
169 let result = ApiCallArgs::parse(&args).unwrap();
170
171 assert_eq!(result.method, "chat.postMessage");
172 assert_eq!(result.params.get("channel"), Some(&"C123456".to_string()));
173 assert_eq!(result.params.get("text"), Some(&"Hello World".to_string()));
174 }
175
176 #[test]
177 fn test_parse_with_json_flag() {
178 let args = vec![
179 "chat.postMessage".to_string(),
180 "--json".to_string(),
181 "channel=C123456".to_string(),
182 ];
183 let result = ApiCallArgs::parse(&args).unwrap();
184
185 assert_eq!(result.method, "chat.postMessage");
186 assert!(result.use_json);
187 assert!(!result.use_get);
188 }
189
190 #[test]
191 fn test_parse_with_get_flag() {
192 let args = vec![
193 "users.info".to_string(),
194 "--get".to_string(),
195 "user=U123456".to_string(),
196 ];
197 let result = ApiCallArgs::parse(&args).unwrap();
198
199 assert_eq!(result.method, "users.info");
200 assert!(!result.use_json);
201 assert!(result.use_get);
202 }
203
204 #[test]
205 fn test_parse_with_both_flags() {
206 let args = vec![
207 "chat.postMessage".to_string(),
208 "--json".to_string(),
209 "--get".to_string(),
210 "channel=C123456".to_string(),
211 ];
212 let result = ApiCallArgs::parse(&args).unwrap();
213
214 assert!(result.use_json);
215 assert!(result.use_get);
216 }
217
218 #[test]
219 fn test_parse_missing_method() {
220 let args: Vec<String> = vec![];
221 let result = ApiCallArgs::parse(&args);
222
223 assert!(result.is_err());
224 match result {
225 Err(ArgsError::MissingMethod) => {}
226 _ => panic!("Expected MissingMethod error"),
227 }
228 }
229
230 #[test]
231 fn test_parse_invalid_key_value() {
232 let args = vec!["chat.postMessage".to_string(), "invalid_arg".to_string()];
233 let result = ApiCallArgs::parse(&args);
234
235 assert!(result.is_err());
236 match result {
237 Err(ArgsError::InvalidKeyValue(arg)) => {
238 assert_eq!(arg, "invalid_arg");
239 }
240 _ => panic!("Expected InvalidKeyValue error"),
241 }
242 }
243
244 #[test]
245 fn test_to_json() {
246 let args = ApiCallArgs {
247 method: "chat.postMessage".to_string(),
248 params: [
249 ("channel".to_string(), "C123456".to_string()),
250 ("text".to_string(), "Hello".to_string()),
251 ]
252 .iter()
253 .cloned()
254 .collect(),
255 use_json: true,
256 use_get: false,
257 token_type: None,
258 raw: false,
259 };
260
261 let json = args.to_json();
262 assert_eq!(json["channel"], "C123456");
263 assert_eq!(json["text"], "Hello");
264 }
265
266 #[test]
267 fn test_to_form() {
268 let args = ApiCallArgs {
269 method: "chat.postMessage".to_string(),
270 params: [
271 ("channel".to_string(), "C123456".to_string()),
272 ("text".to_string(), "Hello".to_string()),
273 ]
274 .iter()
275 .cloned()
276 .collect(),
277 use_json: false,
278 use_get: false,
279 token_type: None,
280 raw: false,
281 };
282
283 let form = args.to_form();
284 assert_eq!(form.len(), 2);
285 assert!(form.contains(&("channel".to_string(), "C123456".to_string())));
286 assert!(form.contains(&("text".to_string(), "Hello".to_string())));
287 }
288
289 #[test]
290 fn test_parse_token_type_space_separated() {
291 let args = vec![
292 "chat.postMessage".to_string(),
293 "--token-type".to_string(),
294 "user".to_string(),
295 "channel=C123456".to_string(),
296 ];
297 let result = ApiCallArgs::parse(&args).unwrap();
298
299 assert_eq!(result.method, "chat.postMessage");
300 assert_eq!(result.token_type, Some(TokenType::User));
301 }
302
303 #[test]
304 fn test_parse_token_type_equals_format() {
305 let args = vec![
306 "chat.postMessage".to_string(),
307 "--token-type=bot".to_string(),
308 "channel=C123456".to_string(),
309 ];
310 let result = ApiCallArgs::parse(&args).unwrap();
311
312 assert_eq!(result.method, "chat.postMessage");
313 assert_eq!(result.token_type, Some(TokenType::Bot));
314 }
315
316 #[test]
317 fn test_parse_token_type_both_formats() {
318 let args1 = vec![
320 "users.info".to_string(),
321 "--token-type".to_string(),
322 "bot".to_string(),
323 ];
324 let result1 = ApiCallArgs::parse(&args1).unwrap();
325 assert_eq!(result1.token_type, Some(TokenType::Bot));
326
327 let args2 = vec!["users.info".to_string(), "--token-type=user".to_string()];
329 let result2 = ApiCallArgs::parse(&args2).unwrap();
330 assert_eq!(result2.token_type, Some(TokenType::User));
331 }
332}