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                logging::warning("Top entry insertion is not yet fully migrated to the card system");
177                logging::warning("This will be improved in a future version");
178                
179                // For now, we'll do a simple implementation
180                let file_path = file.ok_or_else(|| PocketError::Cli("Missing file path".to_string()))?;
181                
182                // Get the most recent entry
183                let mut list_args = vec!["--limit".to_string(), "1".to_string()];
184                
185                // Execute the search command and capture output
186                // This is a workaround until we have proper inter-card communication
187                // TODO: Implement proper inter-card communication
188                
189                logging::error("Top entry insertion not yet implemented in the card system");
190                return Err(PocketError::Cli("Operation not yet supported in the card system".to_string()));
191            } else {
192                return Err(PocketError::Cli("Missing entry ID or --top flag for insert".to_string()));
193            }
194        },
195        
196        Commands::Reload => {
197            logging::info("Reloading all extensions and cards...");
198            
199            // Re-initialize the card manager
200            card_manager = CardManager::new(card_dir.clone());
201            card_manager.load_cards()
202                .map_err(|e| PocketError::Card(format!("Failed to reload cards: {}", e)))?;
203            
204            logging::success("Extensions and cards reloaded successfully");
205        },
206        
207        Commands::ShowHelp { command, extensions } => {
208            if extensions {
209                // Show card commands
210                let commands = card_manager.list_commands();
211                
212                println!("{}", logging::header("Available extensions:"));
213                for (card_name, card_commands) in commands {
214                    println!("\n{}", logging::title(&card_name));
215                    for cmd in card_commands {
216                        println!("  {} - {}", logging::key(&cmd.name), cmd.description);
217                        println!("    Usage: {}", cmd.usage);
218                    }
219                }
220            } else if let Some(cmd) = command {
221                // Show help for a specific command
222                // TODO: Implement this with card system
223                logging::warning("Command-specific help not yet implemented in the card system");
224                logging::warning("This will be improved in a future version");
225            } else {
226                // Show general help
227                print_custom_help();
228            }
229        },
230        
231        Commands::Lint { workflow } => {
232            // TODO: Migrate to card system
233            logging::warning("Lint command not yet migrated to the card system");
234            logging::warning("This will be implemented in a future version");
235        },
236        
237        Commands::DeleteWorkflow { name } => {
238            // TODO: Migrate to card system
239            logging::warning("DeleteWorkflow command not yet migrated to the card system");
240            logging::warning("This will be implemented in a future version");
241        },
242        
243        Commands::Version => {
244            // Show version information
245            println!("Pocket CLI v{}", env!("CARGO_PKG_VERSION"));
246            println!("A powerful tool for managing code snippets and shell integrations");
247        },
248        
249        Commands::Edit { id, force, backpack } => {
250            // Build the arguments for the core card
251            let mut args = vec![id];
252            
253            if force {
254                args.push("--force".to_string());
255            }
256            
257            if let Some(b) = backpack {
258                args.push("--backpack".to_string());
259                args.push(b);
260            }
261            
262            // TODO: Migrate to card system
263            logging::warning("Edit command not yet fully migrated to the card system");
264            logging::warning("This will be improved in a future version");
265        },
266        
267        Commands::Execute { name, args } => {
268            // TODO: Migrate to card system
269            logging::warning("Execute command not yet migrated to the card system");
270            logging::warning("This will be implemented in a future version");
271        },
272        
273        Commands::Cards { operation } => {
274            match operation {
275                Some(CardOperation::List { detail }) => {
276                    // List all cards
277                    println!("{}", logging::header("Available cards:"));
278                    for (name, version, enabled) in card_manager.list_cards() {
279                        let status = if enabled {
280                            "[Enabled]".green().bold()
281                        } else {
282                            "[Disabled]".yellow().bold()
283                        };
284                        
285                        println!("{} {} v{}", status, logging::title(&name), version);
286                        
287                        // List commands for this card
288                        if detail {
289                            if let Ok(commands) = card_manager.get_card_commands(&name) {
290                                for cmd in commands {
291                                    println!("  - {}: {}", cmd.name, cmd.description);
292                                }
293                            }
294                        }
295                        
296                        println!();
297                    }
298                },
299                
300                Some(CardOperation::Enable { name }) => {
301                    // Enable a card
302                    card_manager.enable_card(&name)
303                        .map_err(|e| PocketError::Card(format!("Failed to enable card {}: {}", name, e)))?;
304                    
305                    logging::success(&format!("Card {} enabled", name));
306                },
307                
308                Some(CardOperation::Disable { name }) => {
309                    // Disable a card
310                    card_manager.disable_card(&name)
311                        .map_err(|e| PocketError::Card(format!("Failed to disable card {}: {}", name, e)))?;
312                    
313                    logging::success(&format!("Card {} disabled", name));
314                },
315                
316                Some(CardOperation::Add { name, url }) => {
317                    // Add a new card
318                    card_manager.register_card_config(&name, &url)
319                        .map_err(|e| PocketError::Card(format!("Failed to add card {}: {}", name, e)))?;
320                    
321                    logging::success(&format!("Card {} added from {}", name, url));
322                },
323                
324                Some(CardOperation::Remove { name, force }) => {
325                    // Remove a card
326                    if !force {
327                        println!("Are you sure you want to remove card {}? [y/N]", name);
328                        let mut input = String::new();
329                        std::io::stdin().read_line(&mut input)
330                            .map_err(|e| PocketError::Cli(format!("Failed to read input: {}", e)))?;
331                        
332                        if !input.trim().eq_ignore_ascii_case("y") {
333                            logging::info("Operation cancelled");
334                            return Ok(());
335                        }
336                    }
337                    
338                    card_manager.remove_card_config(&name)
339                        .map_err(|e| PocketError::Card(format!("Failed to remove card {}: {}", name, e)))?;
340                    
341                    logging::success(&format!("Card {} removed", name));
342                },
343                
344                Some(CardOperation::Build { name, release }) => {
345                    // Build a card
346                    card_manager.build_card(&name, release)
347                        .map_err(|e| PocketError::Card(format!("Failed to build card {}: {}", name, e)))?;
348                    
349                    logging::success(&format!("Card {} built successfully", name));
350                },
351                
352                Some(CardOperation::Create { name, description }) => {
353                    // Create a new card
354                    card_manager.create_card(&name, &description)
355                        .map_err(|e| PocketError::Card(format!("Failed to create card {}: {}", name, e)))?;
356                    
357                    logging::success(&format!("Card {} created successfully", name));
358                },
359                
360                None => {
361                    // Show help for the cards command
362                    println!("{}", logging::header("Card Management:"));
363                    println!("  Use the following commands to manage cards:");
364                    println!("    pocket cards list       - List all cards");
365                    println!("    pocket cards enable     - Enable a card");
366                    println!("    pocket cards disable    - Disable a card");
367                    println!("    pocket cards add        - Add a new card");
368                    println!("    pocket cards remove     - Remove a card");
369                    println!("    pocket cards build      - Build a card");
370                    println!("    pocket cards create     - Create a new card template");
371                    println!("");
372                    println!("  For more information, run: pocket help cards");
373                }
374            }
375        },
376        
377        Commands::Blend { script_file, executable, command } => {
378            match command {
379                Some(BlendCommands::Edit { hook_name }) => {
380                    // Build the arguments for the blend card
381                    let args = vec![hook_name];
382                    
383                    // Execute the command
384                    card_manager.execute_command("blend", "edit", &args)
385                        .map_err(|e| PocketError::Card(format!("Failed to edit hook: {}", e)))?;
386                },
387                
388                Some(BlendCommands::List) => {
389                    // Execute the command
390                    card_manager.execute_command("blend", "list", &[])
391                        .map_err(|e| PocketError::Card(format!("Failed to list hooks: {}", e)))?;
392                },
393                
394                Some(BlendCommands::Run { hook_name, args }) => {
395                    // Build the arguments for the blend card
396                    let mut run_args = vec![hook_name];
397                    run_args.extend(args.iter().cloned());
398                    
399                    // Execute the command
400                    card_manager.execute_command("blend", "run", &run_args)
401                        .map_err(|e| PocketError::Card(format!("Failed to run hook: {}", e)))?;
402                },
403                
404                None => {
405                    // Add a script
406                    if let Some(script_path) = script_file {
407                        let mut args = vec![script_path];
408                        
409                        if executable {
410                            args.push("--executable".to_string());
411                        }
412                        
413                        // Execute the command
414                        card_manager.execute_command("blend", "add", &args)
415                            .map_err(|e| PocketError::Card(format!("Failed to add hook: {}", e)))?;
416                    } else {
417                        // Show help for the blend command
418                        println!("{}", logging::header("Blend Command:"));
419                        println!("  Use the following syntax to blend shell scripts:");
420                        println!("    pocket blend <script_file>           - Add a shell extension (sourced at shell startup)");
421                        println!("    pocket blend --executable <script>   - Add an executable hook command (run with @name)");
422                        println!("");
423                        println!("  Other commands:");
424                        println!("    pocket blend list                    - List all installed hooks");
425                        println!("    pocket blend edit <hook_name>        - Edit an existing hook");
426                        println!("    pocket blend run <hook_name> [args]  - Run a hook directly");
427                        println!("");
428                        println!("  For more information, run: pocket help blend");
429                    }
430                }
431            }
432        },
433    }
434    
435    Ok(())
436}
437
438/// Print custom help message
439fn print_custom_help() {
440    println!("{}", logging::header("Pocket CLI Help"));
441    println!("A CLI tool for saving, organizing, and retrieving code snippets");
442    println!("with integrated version control and shell integration");
443    println!("");
444    
445    println!("{}", logging::header("Core Commands:"));
446    println!("  {} - Add content to your pocket storage", logging::key("add"));
447    println!("  {} - Display all pocket entries", logging::key("list"));
448    println!("  {} - Remove an entry from storage", logging::key("remove"));
449    println!("  {} - Create a new backpack for organizing entries", logging::key("create"));
450    println!("  {} - Find entries across all backpacks", logging::key("search"));
451    println!("  {} - Insert an entry into a file", logging::key("insert"));
452    println!("  {} - Reload all extensions", logging::key("reload"));
453    println!("  {} - Display help information", logging::key("help"));
454    println!("  {} - Lint code before adding", logging::key("lint"));
455    println!("  {} - Display version information", logging::key("version"));
456    println!("  {} - Edit an existing entry", logging::key("edit"));
457    println!("  {} - Execute a script", logging::key("execute"));
458    println!("");
459    
460    println!("{}", logging::header("Extension Commands:"));
461    println!("  {} - Manage extensions/cards", logging::key("cards"));
462    println!("  {} - Blend shell scripts into your environment", logging::key("blend"));
463    println!("");
464    
465    println!("For more detailed help on a specific command, run:");
466    println!("  pocket help <command>");
467    println!("");
468    
469    println!("To see all extensions and their commands, run:");
470    println!("  pocket help --extensions");
471    println!("");
472}