mail2couch/rust/src/cli.rs
Ole-Morten Duesund 322fb094a5 feat: add --dry-run mode to Rust implementation
Add comprehensive dry-run functionality to the Rust implementation that allows
users to test their configuration without making any changes to CouchDB:

- Added --dry-run/-n command line flag with clap argument parsing
- Extended CommandLineArgs struct with dry_run field
- Updated bash completion script to include new flag
- Comprehensive dry-run logic throughout sync coordinator:
  - Skip database creation with informative logging
  - Skip sync metadata retrieval and use config fallback
  - Skip deleted message handling in sync mode
  - Skip message and attachment storage with detailed simulation
  - Skip sync metadata updates with summary information
- Enhanced summary output to clearly indicate dry-run vs normal mode
- Updated all tests to include new dry_run field
- Maintains all IMAP operations for realistic mail discovery testing

This brings the Rust implementation to feature parity with the Go version
for safe configuration testing as identified in ANALYSIS.md.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:26:01 +02:00

125 lines
No EOL
4.1 KiB
Rust

//! 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::<String>("config").map(|s| s.clone()),
max_messages: matches.get_one::<u32>("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
}
}