//! Command line interface for mail2couch //! //! This module handles command line argument parsing and bash completion generation, //! matching the behavior of the Go implementation. use clap::{Arg, ArgAction, Command}; use std::env; use std::path::Path; use crate::config::CommandLineArgs; /// Parse command line arguments using GNU-style options pub fn parse_command_line() -> CommandLineArgs { let app = Command::new("mail2couch") .version(env!("CARGO_PKG_VERSION")) .about("Email backup utility for CouchDB") .long_about("A powerful email backup utility that synchronizes mail from IMAP accounts to CouchDB databases with intelligent incremental sync, comprehensive filtering, and native attachment support.") .arg(Arg::new("config") .short('c') .long("config") .value_name("FILE") .help("Path to configuration file") .action(ArgAction::Set)) .arg(Arg::new("max-messages") .short('m') .long("max-messages") .value_name("N") .help("Maximum number of messages to process per mailbox per run (0 = no limit)") .value_parser(clap::value_parser!(u32)) .action(ArgAction::Set)) .arg(Arg::new("dry-run") .short('n') .long("dry-run") .help("Show what would be done without making any changes") .action(ArgAction::SetTrue)) .arg(Arg::new("generate-bash-completion") .long("generate-bash-completion") .help("Generate bash completion script and exit") .action(ArgAction::SetTrue)); let matches = app.get_matches(); // Handle bash completion generation if matches.get_flag("generate-bash-completion") { generate_bash_completion(); std::process::exit(0); } CommandLineArgs { config_path: matches.get_one::("config").map(|s| s.clone()), max_messages: matches.get_one::("max-messages").copied(), dry_run: matches.get_flag("dry-run"), generate_bash_completion: matches.get_flag("generate-bash-completion"), help: false, // Using clap's built-in help } } /// Generate bash completion script for mail2couch pub fn generate_bash_completion() { let app_name = env::args().next() .map(|path| { Path::new(&path).file_name() .and_then(|name| name.to_str()) .unwrap_or("mail2couch") .to_string() }) .unwrap_or_else(|| "mail2couch".to_string()); let script = format!(r#"#!/bin/bash # Bash completion script for {} # Generated automatically by {} --generate-bash-completion _{}_completions() {{ local cur prev words cword _init_completion || return case $prev in -c|--config) # Complete config files (*.json) _filedir "json" return ;; -m|--max-messages) # Complete with numbers, suggest common values COMPREPLY=($(compgen -W "10 50 100 500 1000" -- "$cur")) return ;; esac if [[ $cur == -* ]]; then # Complete with available options local opts="-c --config -m --max-messages -n --dry-run -h --help --generate-bash-completion" COMPREPLY=($(compgen -W "$opts" -- "$cur")) return fi # No default completion for other cases }} # Register the completion function complete -F _{}_completions {} # Enable completion for common variations of the command name if [[ "$({} --help 2>/dev/null)" =~ "mail2couch" ]]; then complete -F _{}_completions mail2couch fi "#, app_name, app_name, app_name, app_name, app_name, app_name, app_name); print!("{}", script); } #[cfg(test)] mod tests { use super::*; #[test] fn test_bash_completion_generation() { // Test that bash completion generation doesn't panic // This is a basic smoke test let _output = std::panic::catch_unwind(|| { generate_bash_completion(); }); // Just verify it doesn't panic, we can't easily test the output without capturing stdout } }