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