1use std::fmt;
7
8use crate::core::collection_manager::CollectionManager;
9use crate::helper;
10use crate::models::collection::Method;
11use clap::Subcommand;
12use colored::Colorize;
13
14use super::request::{RequestCommands, RequestData};
15
16#[derive(Clone, Subcommand)]
17pub enum ManagerCommands {
18 #[clap(about = "List collections and endpoints")]
19 List {
20 #[clap(short = 'c', long = "col", default_value = "", required = false)]
21 col: String,
22
23 #[clap(short = 'e', long = "endpoint", default_value = "", required = false)]
24 endpoint: String,
25
26 #[clap(short = 'q', long = "quiet", default_value = "false")]
27 quiet: bool,
28
29 #[clap(short, long, default_value = "false")]
30 verbose: bool,
31 },
32 #[clap(about = "Update a collection or endpoint headers and body")]
33 Update {
34 collection: String,
35
36 #[clap(short = 'e', long, default_value = "", required = false)]
37 endpoint: String,
38
39 #[clap(short = 'u', long, default_value = "", required = false)]
40 url: String,
41
42 #[clap(
43 short = 'H',
44 long = "header",
45 value_parser = RequestData::parse_header,
46 value_name = "KEY:VALUE",
47 num_args = 1..,
48 required = false
49 )]
50 headers: Vec<(String, String)>,
51
52 #[clap(short = 'b', long, default_value = "", required = false)]
53 body: String,
54 },
55 #[clap(about = "Delete a collection or endpoint")]
56 Delete {
57 collection: String,
58
59 #[clap(short = 'e', long, default_value = "", required = false)]
60 endpoint: String,
61
62 #[clap(short, long, default_value = "false")]
63 yes: bool,
64 },
65 #[clap(about = "Copy a collection or endpoint")]
66 Copy {
67 collection: String,
68
69 #[clap(short = 'e', long, default_value = "", required = false)]
70 endpoint: String,
71
72 #[clap(short = 'c', long, default_value = "false", required = false)]
73 to_col: bool,
74
75 new_name: String,
76 },
77 #[clap(about = "Add a new collection")]
78 Col {
79 name: String,
80 url: String,
81
82 #[clap(
83 short = 'H',
84 long = "header",
85 value_parser = RequestData::parse_header,
86 value_name = "KEY:VALUE",
87 num_args = 1..,
88 required = false
89 )]
90 headers: Vec<(String, String)>,
91 },
92 #[clap(about = "Add a new endpoint to a collection")]
93 Endpoint {
94 collection: String,
95 name: String,
96 path: String,
97
98 #[clap(short = 'm', long, default_value = "GET")]
99 method: String,
100
101 #[clap(
102 short = 'H',
103 long = "header",
104 value_parser = RequestData::parse_header,
105 value_name = "KEY:VALUE",
106 num_args = 1..,
107 required = false
108 )]
109 headers: Vec<(String, String)>,
110
111 #[clap(short = 'b', long, default_value = "", required = false)]
112 body: String,
113 },
114}
115
116impl fmt::Display for ManagerCommands {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 ManagerCommands::List {
120 col,
121 endpoint,
122 quiet,
123 verbose,
124 } => write!(
125 f,
126 "List Command: col: '{}', endpoint: '{}', quiet: {}, verbose: {}",
127 col, endpoint, quiet, verbose
128 ),
129 ManagerCommands::Update {
130 collection,
131 endpoint,
132 url: _,
133 headers,
134 body,
135 } => {
136 write!(
137 f,
138 "Update Command: collection: '{}', endpoint: '{}', headers: {:?}, body: '{}'",
139 collection, endpoint, headers, body
140 )
141 }
142 ManagerCommands::Delete {
143 collection,
144 endpoint,
145 yes,
146 } => {
147 write!(
148 f,
149 "Delete Command: collection: '{}', endpoint: '{}', yes: {}",
150 collection, endpoint, yes
151 )
152 }
153 ManagerCommands::Copy {
154 collection,
155 endpoint,
156 to_col,
157 new_name,
158 } => {
159 write!(
160 f,
161 "Copy Command: collection: '{}', endpoint: '{}', To Col {}, new_name: '{}'",
162 collection, endpoint, to_col, new_name
163 )
164 }
165 ManagerCommands::Col { name, url, headers } => {
166 write!(
167 f,
168 "Col Command: name: '{}', url: '{}', headers: {:?}",
169 name, url, headers
170 )
171 }
172 ManagerCommands::Endpoint {
173 collection,
174 name,
175 path,
176 method,
177 headers,
178 body,
179 } => {
180 write!(f, "Endpoint Command: collection: '{}', name: '{}', path: '{}', method: '{}', headers: {:?}, body: '{}'",
181 collection, name, path, method, headers, body)
182 }
183 }
184 }
185}
186
187impl ManagerCommands {
188 fn get_manager() -> CollectionManager {
190 CollectionManager::default()
191 }
192
193 pub fn get_endpoint_command(collection: &str, endpoint: &str) -> Option<RequestCommands> {
195 let manager = Self::get_manager();
196 let col = manager.get_collection(collection).ok()?;
197 let req = manager.get_endpoint(collection, endpoint).ok()?;
198
199 let data = RequestData {
200 url: format!("{}{}", col.url, req.endpoint),
201 headers: manager
202 .get_endpoint_headers(collection, endpoint)
203 .unwrap_or_default(),
204 body: req.body.clone().unwrap_or_default(),
205 };
206
207 Some(match req.method {
208 Method::Get => RequestCommands::Get { data },
209 Method::Post => RequestCommands::Post { data },
210 Method::Delete => RequestCommands::Delete { data },
211 Method::Patch => RequestCommands::Patch { data },
212 Method::Put => RequestCommands::Put { data },
213 })
214 }
215
216 pub fn run(&self) -> Result<String, Box<dyn std::error::Error>> {
217 let manager = Self::get_manager();
218
219 match self {
220 Self::List {
222 col,
223 endpoint,
224 quiet,
225 verbose,
226 } => {
227 let collections = manager.load_collections()?;
228 if collections.is_empty() {
229 return Err("No collections found.".into());
230 } else {
231 for collection in collections {
232 if !col.is_empty() && &collection.name != col {
233 continue;
234 }
235 println!(
236 "[{}] - {}",
237 collection.name.bright_magenta(),
238 collection.url
239 );
240 if *quiet {
241 continue;
242 }
243 if !collection.headers.is_empty() {
244 println!(" Headers:");
245 for (key, value) in &collection.headers {
246 println!(" {}: {}", key.bright_cyan(), value.bright_cyan());
247 }
248 }
249 if let Some(requests) = collection.requests {
250 for request in requests {
251 if !endpoint.is_empty() && &request.name != endpoint {
252 continue;
253 }
254 println!(
255 " [{}] {} - {} - {} - {}",
256 request.name.bright_yellow(),
257 request.method.to_string().bright_green(),
258 request.endpoint.bright_white(),
259 request.headers.len(),
260 request.body.as_ref().map_or(0, |b| b.len())
261 );
262 if *verbose {
263 if !request.headers.is_empty() {
265 println!(" Headers:");
266 for (key, value) in &request.headers {
267 println!(
268 " {}: {}",
269 key.bright_cyan(),
270 value.bright_cyan()
271 );
272 }
273 }
274 if request.body.is_some() {
276 println!(" Body:");
277 if let Some(body) = &request.body {
278 println!(" {}", body.bright_cyan());
279 };
280 }
281 }
282 }
283 }
284 }
285 }
286 }
287
288 Self::Delete {
290 collection,
291 endpoint,
292 yes,
293 } => {
294 if endpoint.is_empty() {
295 println!("Deleting collection '{}'", collection);
297 let confirm = if !yes {
298 helper::confirm("Are you sure you want to delete this collection?")
299 } else {
300 true
301 };
302 if confirm {
303 manager.delete_collection(collection)?;
304 println!("Collection deleted successfully!");
305 } else {
306 return Err("Deletion cancelled.".into());
307 }
308 } else {
309 println!("Deleting endpoint '{}'", endpoint);
311 let confirm = if !yes {
312 helper::confirm("Are you sure you want to delete this endpoint?")
313 } else {
314 true
315 };
316 if confirm {
317 manager.delete_endpoint(collection, endpoint)?;
318 println!("Endpoint deleted successfully!");
319 } else {
320 return Err("Deletion cancelled.".into());
321 }
322 }
323 }
324
325 Self::Copy {
327 collection,
328 endpoint,
329 to_col,
330 new_name,
331 } => {
332 if endpoint.is_empty() {
333 manager.copy_collection(collection, new_name)?;
335 } else if *to_col {
336 manager.copy_endpoint(collection, endpoint, new_name, Some(new_name))?;
338 } else {
339 manager.copy_endpoint(collection, endpoint, new_name, None)?;
341 }
342 println!("Copy command successful!");
343 }
344
345 Self::Update {
347 collection,
348 endpoint,
349 url,
350 headers,
351 body,
352 } => {
353 if endpoint.is_empty() {
354 let url_opt = if url.is_empty() {
356 None
357 } else {
358 Some(url.as_str())
359 };
360 let headers_opt = if headers.is_empty() {
361 None
362 } else {
363 Some(headers.clone())
364 };
365 manager.update_collection(collection, url_opt, headers_opt)?;
366 } else {
367 let url_opt = if url.is_empty() {
369 None
370 } else {
371 Some(url.as_str())
372 };
373 let headers_opt = if headers.is_empty() {
374 None
375 } else {
376 Some(headers.clone())
377 };
378 let body_opt = if body.is_empty() {
379 Some(String::new()) } else {
381 Some(body.clone())
382 };
383 manager.update_endpoint(
384 collection,
385 endpoint,
386 url_opt,
387 headers_opt,
388 body_opt,
389 )?;
390 }
391 println!("Collection updated successfully!");
392 }
393
394 Self::Col { name, url, headers } => {
396 let exists = manager.get_collection(name).is_ok();
397 manager.add_collection(name, url, headers.clone())?;
398 if exists {
399 eprintln!("Collection with name '{}' already exists.", name);
400 println!("Collection updated successfully!");
401 } else {
402 println!("Collection added successfully!");
403 }
404 }
405
406 Self::Endpoint {
408 collection,
409 name,
410 path,
411 method,
412 headers,
413 body,
414 } => {
415 let method: Method = method
416 .to_uppercase()
417 .parse()
418 .map_err(|_| format!("Invalid HTTP method: {}", method))?;
419
420 let body_opt = if body.trim().is_empty() {
421 None
422 } else {
423 Some(body.clone())
424 };
425
426 manager.add_endpoint(collection, name, path, method, headers.clone(), body_opt)?;
427 println!("Endpoint added successfully!");
428 }
429 }
430
431 Ok("".to_string())
432 }
433}