pocket_cli/cli/
handler.rs

1use crate::cli::{Cli, Commands, CardOperation, BlendCommands};
2use crate::cards::CardManager;
3use crate::errors::{PocketError, PocketResult, IntoPocketError};
4use crate::logging;
5use log::{debug, info, warn, error, LevelFilter};
6use std::path::PathBuf;
7use colored::Colorize;
8
9/// Handle the CLI command
10pub fn handle_command(cli: Cli) -> PocketResult<()> {
11    // Set up logging based on verbosity
12    let log_level = match cli.verbose {
13        0 => LevelFilter::Warn,
14        1 => LevelFilter::Info,
15        2 => LevelFilter::Debug,
16        _ => LevelFilter::Trace,
17    };
18    logging::init(log_level);
19    
20    debug!("Starting pocket CLI with verbosity level {}", cli.verbose);
21    
22    // Get the home directory
23    let home_dir = std::env::var("HOME")
24        .map_err(|_| PocketError::Config("HOME environment variable not set".to_string()))?;
25    let data_dir = PathBuf::from(&home_dir).join(".pocket");
26    
27    // Initialize the card manager
28    let card_dir = data_dir.join("cards");
29    let mut card_manager = CardManager::new(card_dir.clone());
30    card_manager.load_cards()
31        .map_err(|e| PocketError::Card(format!("Failed to load cards: {}", e)))?;
32    
33    // Handle the command
34    match cli.command {
35        Commands::Add { file, message, editor, backpack, clipboard, summarize } => {
36            // Build the arguments for the snippet card
37            let mut args = Vec::new();
38            
39            if let Some(f) = file {
40                args.push(format!("--file={}", f));
41            }
42            
43            if let Some(m) = message {
44                args.push(format!("--message={}", m));
45            }
46            
47            if editor {
48                args.push("--editor".to_string());
49            }
50            
51            if let Some(b) = backpack {
52                args.push(format!("--backpack={}", b));
53            }
54            
55            if clipboard {
56                args.push("--clipboard".to_string());
57            }
58            
59            if let Some(s) = summarize {
60                args.push(format!("--summarize={}", s));
61            }
62            
63            // Execute the command
64            card_manager.execute_command("snippet", "add", &args)
65                .map_err(|e| PocketError::Card(format!("Failed to add snippet: {}", e)))?;
66        },
67        
68        Commands::List { all, backpack, json, limit } => {
69            // Build the arguments for the core card
70            let mut args = Vec::new();
71            
72            if all {
73                args.push("--include-backpacks".to_string());
74            }
75            
76            if let Some(b) = backpack {
77                args.push("--backpack".to_string());
78                args.push(b);
79            }
80            
81            if json {
82                args.push("--json".to_string());
83            }
84            
85            args.push("--limit".to_string());
86            args.push(limit.to_string());
87            
88            // Execute the command
89            card_manager.execute_command("core", "list", &args)
90                .map_err(|e| PocketError::Card(format!("Failed to list entries: {}", e)))?;
91        },
92        
93        Commands::Remove { id, force, backpack } => {
94            // Build the arguments for the core card
95            let mut args = vec![id];
96            
97            if force {
98                args.push("--force".to_string());
99            }
100            
101            if let Some(b) = backpack {
102                args.push("--backpack".to_string());
103                args.push(b);
104            }
105            
106            // Execute the command
107            card_manager.execute_command("core", "remove", &args)
108                .map_err(|e| PocketError::Card(format!("Failed to remove entry: {}", e)))?;
109        },
110        
111        Commands::Create { name, description } => {
112            // Build the arguments for the core card
113            let mut args = vec![name];
114            
115            if let Some(d) = description {
116                args.push("--description".to_string());
117                args.push(d);
118            }
119            
120            // Execute the command
121            card_manager.execute_command("core", "create-backpack", &args)
122                .map_err(|e| PocketError::Card(format!("Failed to create backpack: {}", e)))?;
123        },
124        
125        Commands::Search { query, limit, backpack, exact, package } => {
126            if package {
127                // Special case for package search (not yet migrated to card system)
128                logging::warning("Package search is not yet migrated to the card system");
129                logging::warning("This will be implemented in a future version");
130                return Ok(());
131            }
132            
133            // Build the arguments for the core card
134            let mut args = vec![query];
135            
136            args.push("--limit".to_string());
137            args.push(limit.to_string());
138            
139            if let Some(b) = backpack {
140                args.push("--backpack".to_string());
141                args.push(b);
142            }
143            
144            if exact {
145                args.push("--exact".to_string());
146            }
147            
148            // Execute the command
149            card_manager.execute_command("core", "search", &args)
150                .map_err(|e| PocketError::Card(format!("Failed to search entries: {}", e)))?;
151        },
152        
153        Commands::Insert { id, file, top, no_confirm, delimiter } => {
154            if let Some(id) = id {
155                if let Some(file_path) = file {
156                    // Build the arguments for the core card
157                    let mut args = vec![id, file_path];
158                    
159                    if no_confirm {
160                        args.push("--no-confirm".to_string());
161                    }
162                    
163                    if let Some(d) = delimiter {
164                        args.push("--delimiter".to_string());
165                        args.push(d);
166                    }
167                    
168                    // Execute the command
169                    card_manager.execute_command("core", "insert", &args)
170                        .map_err(|e| PocketError::Card(format!("Failed to insert entry: {}", e)))?;
171                } else {
172                    return Err(PocketError::Cli("Missing file path for insert".to_string()));
173                }
174            } else if top {
175                // Handle top entry insertion (not yet fully migrated to card system)
176                return Err(PocketError::Cli("Operation not yet supported in the card system".to_string()));
177            } else {
178                return Err(PocketError::Cli("Missing entry ID for insert".to_string()));
179            }
180        },
181        
182        Commands::Reload => {
183            logging::info("Reloading all extensions and cards...");
184            
185            // Re-initialize the card manager
186            card_manager = CardManager::new(card_dir.clone());
187            card_manager.load_cards()
188                .map_err(|e| PocketError::Card(format!("Failed to reload cards: {}", e)))?;
189            
190            logging::success("Extensions and cards reloaded successfully");
191        },
192        
193        Commands::ShowHelp { command, extensions } => {
194            if extensions {
195                // Show card commands
196                let commands = card_manager.list_commands();
197                
198                println!("{}", logging::header("Available extensions:"));
199                for (card_name, card_commands) in commands {
200                    println!("\n{}", logging::title(&card_name));
201                    for cmd in card_commands {
202                        println!("  {} - {}", logging::key(&cmd.name), cmd.description);
203                        println!("    Usage: {}", cmd.usage);
204                    }
205                }
206            } else if let Some(cmd) = command {
207                // Show help for a specific command
208                // TODO: Implement this with card system
209                logging::warning("Command-specific help not yet implemented in the card system");
210                logging::warning("This will be improved in a future version");
211            } else {
212                // Show general help
213                print_custom_help();
214            }
215        },
216        
217        Commands::Lint { workflow } => {
218            // TODO: Migrate to card system
219            logging::warning("Lint command not yet migrated to the card system");
220            logging::warning("This will be implemented in a future version");
221        },
222        
223        Commands::DeleteWorkflow { name } => {
224            // TODO: Migrate to card system
225            logging::warning("DeleteWorkflow command not yet migrated to the card system");
226            logging::warning("This will be implemented in a future version");
227        },
228        
229        Commands::Version => {
230            // Show version information
231            println!("Pocket CLI v{}", env!("CARGO_PKG_VERSION"));
232            println!("A powerful tool for managing code snippets and shell integrations");
233        },
234        
235        Commands::Edit { id, force, backpack } => {
236            // Build the arguments for the core card
237            let mut args = vec![id];
238            
239            if force {
240                args.push("--force".to_string());
241            }
242            
243            if let Some(b) = backpack {
244                args.push("--backpack".to_string());
245                args.push(b);
246            }
247            
248            // TODO: Migrate to card system
249            logging::warning("Edit command not yet fully migrated to the card system");
250            logging::warning("This will be improved in a future version");
251        },
252        
253        Commands::Execute { name, args } => {
254            // TODO: Migrate to card system
255            logging::warning("Execute command not yet migrated to the card system");
256            logging::warning("This will be implemented in a future version");
257        },
258        
259        Commands::Cards { operation } => {
260            match operation {
261                Some(CardOperation::List { detail }) => {
262                    // List all cards
263                    println!("{}", logging::header("Available cards:"));
264                    for (name, version, enabled) in card_manager.list_cards() {
265                        let status = if enabled {
266                            "[Enabled]".green().bold()
267                        } else {
268                            "[Disabled]".yellow().bold()
269                        };
270                        
271                        println!("{} {} v{}", status, logging::title(&name), version);
272                        
273                        // List commands for this card
274                        if detail {
275                            if let Ok(commands) = card_manager.get_card_commands(&name) {
276                                for cmd in commands {
277                                    println!("  - {}: {}", cmd.name, cmd.description);
278                                }
279                            }
280                        }
281                        
282                        println!();
283                    }
284                },
285                
286                Some(CardOperation::Enable { name }) => {
287                    // Enable a card
288                    card_manager.enable_card(&name)
289                        .map_err(|e| PocketError::Card(format!("Failed to enable card {}: {}", name, e)))?;
290                    
291                    logging::success(&format!("Card {} enabled", name));
292                },
293                
294                Some(CardOperation::Disable { name }) => {
295                    // Disable a card
296                    card_manager.disable_card(&name)
297                        .map_err(|e| PocketError::Card(format!("Failed to disable card {}: {}", name, e)))?;
298                    
299                    logging::success(&format!("Card {} disabled", name));
300                },
301                
302                Some(CardOperation::Add { name, url }) => {
303                    // Add a new card
304                    card_manager.register_card_config(&name, &url)
305                        .map_err(|e| PocketError::Card(format!("Failed to add card {}: {}", name, e)))?;
306                    
307                    logging::success(&format!("Card {} added from {}", name, url));
308                },
309                
310                Some(CardOperation::Remove { name, force }) => {
311                    // Remove a card
312                    if !force {
313                        println!("Are you sure you want to remove card {}? [y/N]", name);
314                        let mut input = String::new();
315                        std::io::stdin().read_line(&mut input)
316                            .map_err(|e| PocketError::Cli(format!("Failed to read input: {}", e)))?;
317                        
318                        if !input.trim().eq_ignore_ascii_case("y") {
319                            logging::info("Operation cancelled");
320                            return Ok(());
321                        }
322                    }
323                    
324                    card_manager.remove_card_config(&name)
325                        .map_err(|e| PocketError::Card(format!("Failed to remove card {}: {}", name, e)))?;
326                    
327                    logging::success(&format!("Card {} removed", name));
328                },
329                
330                Some(CardOperation::Build { name, release }) => {
331                    // Build a card
332                    card_manager.build_card(&name, release)
333                        .map_err(|e| PocketError::Card(format!("Failed to build card {}: {}", name, e)))?;
334                    
335                    logging::success(&format!("Card {} built successfully", name));
336                },
337                
338                Some(CardOperation::Create { name, description }) => {
339                    // Create a new card
340                    card_manager.create_card(&name, &description)
341                        .map_err(|e| PocketError::Card(format!("Failed to create card {}: {}", name, e)))?;
342                    
343                    logging::success(&format!("Card {} created successfully", name));
344                },
345                
346                None => {
347                    // Show help for the cards command
348                    println!("{}", logging::header("Card Management:"));
349                    println!("  Use the following commands to manage cards:");
350                    println!("    pocket cards list       - List all cards");
351                    println!("    pocket cards enable     - Enable a card");
352                    println!("    pocket cards disable    - Disable a card");
353                    println!("    pocket cards add        - Add a new card");
354                    println!("    pocket cards remove     - Remove a card");
355                    println!("    pocket cards build      - Build a card");
356                    println!("    pocket cards create     - Create a new card template");
357                    println!("");
358                    println!("  For more information, run: pocket help cards");
359                }
360            }
361        },
362        
363        Commands::Blend { script_file, executable, command } => {
364            match command {
365                Some(BlendCommands::Edit { hook_name }) => {
366                    // Build the arguments for the blend card
367                    let args = vec![hook_name];
368                    
369                    // Execute the command
370                    card_manager.execute_command("blend", "edit", &args)
371                        .map_err(|e| PocketError::Card(format!("Failed to edit hook: {}", e)))?;
372                },
373                
374                Some(BlendCommands::List) => {
375                    // Execute the command
376                    card_manager.execute_command("blend", "list", &[])
377                        .map_err(|e| PocketError::Card(format!("Failed to list hooks: {}", e)))?;
378                },
379                
380                Some(BlendCommands::Run { hook_name, args }) => {
381                    // Build the arguments for the blend card
382                    let mut run_args = vec![hook_name];
383                    run_args.extend(args.iter().cloned());
384                    
385                    // Execute the command
386                    card_manager.execute_command("blend", "run", &run_args)
387                        .map_err(|e| PocketError::Card(format!("Failed to run hook: {}", e)))?;
388                },
389                
390                None => {
391                    // Add a script
392                    if let Some(script_path) = script_file {
393                        let mut args = vec![script_path];
394                        
395                        if executable {
396                            args.push("--executable".to_string());
397                        }
398                        
399                        // Execute the command
400                        card_manager.execute_command("blend", "add", &args)
401                            .map_err(|e| PocketError::Card(format!("Failed to add hook: {}", e)))?;
402                    } else {
403                        // Show help for the blend command
404                        println!("{}", logging::header("Blend Command:"));
405                        println!("  Use the following syntax to blend shell scripts:");
406                        println!("    pocket blend <script_file>           - Add a shell extension (sourced at shell startup)");
407                        println!("    pocket blend --executable <script>   - Add an executable hook command (run with @name)");
408                        println!("");
409                        println!("  Other commands:");
410                        println!("    pocket blend list                    - List all installed hooks");
411                        println!("    pocket blend edit <hook_name>        - Edit an existing hook");
412                        println!("    pocket blend run <hook_name> [args]  - Run a hook directly");
413                        println!("");
414                        println!("  For more information, run: pocket help blend");
415                    }
416                }
417            }
418        },
419    }
420    
421    Ok(())
422}
423
424/// Print custom help message
425fn print_custom_help() {
426    println!("{}", logging::header("Pocket CLI Help"));
427    println!("A CLI tool for saving, organizing, and retrieving code snippets");
428    println!("with integrated version control and shell integration");
429    println!("");
430    
431    println!("{}", logging::header("Core Commands:"));
432    println!("  {} - Add content to your pocket storage", logging::key("add"));
433    println!("  {} - Display all pocket entries", logging::key("list"));
434    println!("  {} - Remove an entry from storage", logging::key("remove"));
435    println!("  {} - Create a new backpack for organizing entries", logging::key("create"));
436    println!("  {} - Find entries across all backpacks", logging::key("search"));
437    println!("  {} - Insert an entry into a file", logging::key("insert"));
438    println!("  {} - Reload all extensions", logging::key("reload"));
439    println!("  {} - Display help information", logging::key("help"));
440    println!("  {} - Lint code before adding", logging::key("lint"));
441    println!("  {} - Display version information", logging::key("version"));
442    println!("  {} - Edit an existing entry", logging::key("edit"));
443    println!("  {} - Execute a script", logging::key("execute"));
444    println!("");
445    
446    println!("{}", logging::header("Extension Commands:"));
447    println!("  {} - Manage extensions/cards", logging::key("cards"));
448    println!("  {} - Blend shell scripts into your environment", logging::key("blend"));
449    println!("");
450    
451    println!("For more detailed help on a specific command, run:");
452    println!("  pocket help <command>");
453    println!("");
454    
455    println!("To see all extensions and their commands, run:");
456    println!("  pocket help --extensions");
457    println!("");
458}