feat: implement GNU-style command line options with pflag

- Add pflag dependency for POSIX/GNU-style command line parsing
- Replace Go standard flag package with pflag for better UX
- Implement long options with double dashes (--config, --max-messages, --help)
- Add short option aliases with single dashes (-c, -m, -h)
- Update help message with proper formatting and application description
- Update all documentation to reflect new flag syntax
- Update test scripts to use new command line format

GNU-style options provide better usability:
- Long descriptive options with --flag-name format
- Short single-character aliases for common options
- Standard help flag behavior with --help/-h
- Compatible with shell completion and standard conventions

Command line interface now supports:
- --config/-c FILE: Path to configuration file
- --max-messages/-m N: Message processing limit per mailbox
- --help/-h: Show help message and exit

All existing functionality preserved with improved command line experience.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-08-02 15:17:04 +02:00
commit 031dd86b0d
8 changed files with 47 additions and 28 deletions

View file

@ -20,13 +20,13 @@ cd go && go build -o mail2couch .
cd go && ./mail2couch cd go && ./mail2couch
# Run with specific config file # Run with specific config file
cd go && ./mail2couch -config /path/to/config.json cd go && ./mail2couch --config /path/to/config.json
# Run with message limit (useful for large mailboxes) # Run with message limit (useful for large mailboxes)
cd go && ./mail2couch -max-messages 100 cd go && ./mail2couch --max-messages 100
# Run with both config and message limit # Run with both config and message limit
cd go && ./mail2couch -config /path/to/config.json -max-messages 50 cd go && ./mail2couch --config /path/to/config.json --max-messages 50
# Run linting/static analysis # Run linting/static analysis
cd go && go vet ./... cd go && go vet ./...
@ -80,7 +80,7 @@ The application uses `config.json` for configuration with the following structur
### Configuration File Discovery ### Configuration File Discovery
The application automatically searches for configuration files in the following order: The application automatically searches for configuration files in the following order:
1. Path specified by `-config` command line flag 1. Path specified by `--config`/`-c` command line flag
2. `./config.json` (current working directory) 2. `./config.json` (current working directory)
3. `./config/config.json` (config subdirectory) 3. `./config/config.json` (config subdirectory)
4. `~/.config/mail2couch/config.json` (user XDG config directory) 4. `~/.config/mail2couch/config.json` (user XDG config directory)
@ -104,7 +104,7 @@ This design ensures the same `config.json` format will work for both Go and Rust
- ✅ Sync vs Archive mode implementation - ✅ Sync vs Archive mode implementation
- ✅ CouchDB attachment storage for email attachments - ✅ CouchDB attachment storage for email attachments
- ✅ Full message body and attachment handling with MIME multipart support - ✅ Full message body and attachment handling with MIME multipart support
- ✅ Command line argument support (--max-messages flag) - ✅ Command line argument support (GNU-style --max-messages/-m and --config/-c flags)
- ✅ Per-account CouchDB databases for better organization - ✅ Per-account CouchDB databases for better organization
- ✅ Incremental sync functionality with IMAP SEARCH and sync metadata tracking - ✅ Incremental sync functionality with IMAP SEARCH and sync metadata tracking
- ❌ Rust implementation - ❌ Rust implementation

View file

@ -39,7 +39,7 @@ A powerful email backup utility that synchronizes mail from IMAP accounts to Cou
### Operational Features ### Operational Features
- **Automatic Config Discovery**: Finds configuration files in standard locations - **Automatic Config Discovery**: Finds configuration files in standard locations
- **Command Line Control**: Override settings with `--max-messages` and `--config` flags - **Command Line Control**: GNU-style options with `--max-messages`/`-m` and `--config`/`-c` flags
- **Comprehensive Logging**: Detailed output for monitoring and troubleshooting - **Comprehensive Logging**: Detailed output for monitoring and troubleshooting
- **Error Resilience**: Graceful handling of network issues and server problems - **Error Resilience**: Graceful handling of network issues and server problems
@ -105,7 +105,7 @@ The application will:
### Configuration File Discovery ### Configuration File Discovery
mail2couch automatically searches for configuration files in this order: mail2couch automatically searches for configuration files in this order:
1. Path specified by `--config` flag 1. Path specified by `--config`/`-c` flag
2. `./config.json` (current directory) 2. `./config.json` (current directory)
3. `./config/config.json` (config subdirectory) 3. `./config/config.json` (config subdirectory)
4. `~/.config/mail2couch/config.json` (user config directory) 4. `~/.config/mail2couch/config.json` (user config directory)
@ -117,8 +117,9 @@ mail2couch automatically searches for configuration files in this order:
./mail2couch [options] ./mail2couch [options]
Options: Options:
--config PATH Specify configuration file path -c, --config FILE Path to configuration file
--max-messages N Limit messages processed per mailbox per run (0 = unlimited) -m, --max-messages N Limit messages processed per mailbox per run (0 = unlimited)
-h, --help Show help message
``` ```
### Folder Pattern Examples ### Folder Pattern Examples
@ -382,7 +383,7 @@ Complex setup with multiple accounts, filtering, and different sync modes:
- Schedule regular backups of CouchDB databases - Schedule regular backups of CouchDB databases
### Performance Tuning ### Performance Tuning
- Use `--max-messages` to limit processing load - Use `--max-messages`/`-m` to limit processing load
- Run during off-peak hours for large initial syncs - Run during off-peak hours for large initial syncs
- Monitor IMAP server rate limits and connection limits - Monitor IMAP server rate limits and connection limits
- Consider running multiple instances for different accounts - Consider running multiple instances for different accounts
@ -408,7 +409,7 @@ Complex setup with multiple accounts, filtering, and different sync modes:
**Performance Problems**: **Performance Problems**:
- Use date filtering (`since`) for large mailboxes - Use date filtering (`since`) for large mailboxes
- Implement `--max-messages` limits for initial syncs - Implement `--max-messages`/`-m` limits for initial syncs
- Monitor server-side rate limiting - Monitor server-side rate limiting
For detailed troubleshooting, see the [test environment documentation](test/README.md). For detailed troubleshooting, see the [test environment documentation](test/README.md).

View file

@ -2,10 +2,11 @@ package config
import ( import (
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/spf13/pflag"
) )
type Config struct { type Config struct {
@ -87,23 +88,37 @@ type CommandLineArgs struct {
MaxMessages int MaxMessages int
} }
// ParseCommandLine parses command line arguments // ParseCommandLine parses command line arguments using GNU-style options
func ParseCommandLine() *CommandLineArgs { func ParseCommandLine() *CommandLineArgs {
configFlag := flag.String("config", "", "Path to configuration file") var args CommandLineArgs
maxMessagesFlag := flag.Int("max-messages", 0, "Maximum number of messages to process per mailbox per run (0 = no limit)")
flag.Parse()
return &CommandLineArgs{ // Define long options with -- and short options with -
ConfigPath: *configFlag, pflag.StringVarP(&args.ConfigPath, "config", "c", "", "Path to configuration file")
MaxMessages: *maxMessagesFlag, pflag.IntVarP(&args.MaxMessages, "max-messages", "m", 0, "Maximum number of messages to process per mailbox per run (0 = no limit)")
// Add help option
pflag.BoolP("help", "h", false, "Show help message")
pflag.Parse()
// Handle help flag
if help, _ := pflag.CommandLine.GetBool("help"); help {
fmt.Fprintf(os.Stderr, "mail2couch - Email backup utility for CouchDB\n\n")
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Options:\n")
pflag.PrintDefaults()
os.Exit(0)
} }
return &args
} }
// FindConfigFile searches for config.json in the following order: // FindConfigFile searches for config.json in the following order:
// 1. Path specified by -config flag // 1. Path specified by --config/-c flag
// 2. ./config.json (current directory) // 2. ./config.json (current directory)
// 3. ~/.config/mail2couch/config.json (user config directory) // 3. ./config/config.json (config subdirectory)
// 4. ~/.mail2couch.json (user home directory) // 4. ~/.config/mail2couch/config.json (user config directory)
// 5. ~/.mail2couch.json (user home directory)
func FindConfigFile(args *CommandLineArgs) (string, error) { func FindConfigFile(args *CommandLineArgs) (string, error) {
if args.ConfigPath != "" { if args.ConfigPath != "" {
if _, err := os.Stat(args.ConfigPath); err == nil { if _, err := os.Stat(args.ConfigPath); err == nil {

View file

@ -6,6 +6,7 @@ require (
github.com/emersion/go-imap/v2 v2.0.0-beta.5 github.com/emersion/go-imap/v2 v2.0.0-beta.5
github.com/emersion/go-message v0.18.1 github.com/emersion/go-message v0.18.1
github.com/go-kivik/kivik/v4 v4.4.0 github.com/go-kivik/kivik/v4 v4.4.0
github.com/spf13/pflag v1.0.7
) )
require ( require (

View file

@ -24,6 +24,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/flimzy/testy v0.14.0 h1:2nZV4Wa1OSJb3rOKHh0GJqvvhtE03zT+sKnPCI0owfQ= gitlab.com/flimzy/testy v0.14.0 h1:2nZV4Wa1OSJb3rOKHh0GJqvvhtE03zT+sKnPCI0owfQ=
gitlab.com/flimzy/testy v0.14.0/go.mod h1:m3aGuwdXc+N3QgnH+2Ar2zf1yg0UxNdIaXKvC5SlfMk= gitlab.com/flimzy/testy v0.14.0/go.mod h1:m3aGuwdXc+N3QgnH+2Ar2zf1yg0UxNdIaXKvC5SlfMk=

View file

@ -83,7 +83,7 @@ cd ../test
# Run mail2couch with test configuration # Run mail2couch with test configuration
print_status "Running mail2couch with test configuration..." print_status "Running mail2couch with test configuration..."
../go/mail2couch -config config-test.json -max-messages 3 ../go/mail2couch --config config-test.json --max-messages 3
# Verify results # Verify results
print_status "Verifying test results..." print_status "Verifying test results..."
@ -112,7 +112,7 @@ done
# Test sync mode by running again (should show incremental behavior) # Test sync mode by running again (should show incremental behavior)
print_status "Running mail2couch again to test incremental sync..." print_status "Running mail2couch again to test incremental sync..."
../go/mail2couch -config config-test.json -max-messages 3 ../go/mail2couch --config config-test.json --max-messages 3
print_status "🎉 Basic integration tests completed successfully!" print_status "🎉 Basic integration tests completed successfully!"

View file

@ -67,7 +67,7 @@ build_app() {
run_first_sync() { run_first_sync() {
echo -e "\n${BLUE}Running first sync...${NC}" echo -e "\n${BLUE}Running first sync...${NC}"
cd go cd go
./mail2couch -config ../test/config-test.json -max-messages 5 ./mail2couch --config ../test/config-test.json --max-messages 5
cd .. cd ..
} }
@ -138,7 +138,7 @@ EOF
run_incremental_sync() { run_incremental_sync() {
echo -e "\n${BLUE}Running incremental sync...${NC}" echo -e "\n${BLUE}Running incremental sync...${NC}"
cd go cd go
./mail2couch -config ../test/config-test.json -max-messages 10 ./mail2couch --config ../test/config-test.json --max-messages 10
cd .. cd ..
} }

View file

@ -31,7 +31,7 @@ run_test() {
# Run mail2couch and capture output # Run mail2couch and capture output
cd go cd go
if ./mail2couch -config "../test/$config_file" -max-messages "$max_messages" 2>&1; then if ./mail2couch --config "../test/$config_file" --max-messages "$max_messages" 2>&1; then
echo -e "${GREEN}✅ Test completed successfully${NC}" echo -e "${GREEN}✅ Test completed successfully${NC}"
else else
echo -e "${RED}❌ Test failed${NC}" echo -e "${RED}❌ Test failed${NC}"