📝 docs(cli): add comprehensive module documentation and function docs

- Add extensive module-level documentation explaining CLI architecture
- Document all CLI structs with detailed field explanations and usage examples
- Add comprehensive function documentation covering all main workflow functions
- Include safety considerations, error handling, and configuration guidance
- Fix configuration parameter name: credentials -> credential_file
- Document logging levels, exit codes, and environment variable overrides
- Add detailed explanations of rule processing workflow and safety features
This commit is contained in:
Jeremiah Russell
2025-10-20 15:30:47 +01:00
committed by Jeremiah Russell
parent 79373c7387
commit a35b5f9248

View File

@@ -1,3 +1,114 @@
//! # Gmail Message Cull CLI Application
//!
//! A command-line interface for managing Gmail messages with automated retention rules.
//! This CLI provides powerful tools for querying, filtering, and managing Gmail messages
//! based on labels, age, and custom rules with built-in safety features like dry-run mode.
//!
//! ## Overview
//!
//! The CLI is built around three main command categories:
//!
//! - **Labels**: List and inspect Gmail labels for message organization
//! - **Messages**: Query, filter, and perform batch operations on Gmail messages
//! - **Rules**: Configure and execute automated message lifecycle management rules
//!
//! ## Authentication
//!
//! The CLI uses OAuth2 for Gmail API authentication with the following configuration:
//!
//! - **Configuration file**: `~/.cull-gmail/cull-gmail.toml`
//! - **Credential file**: OAuth2 credentials from Google Cloud Platform
//! - **Token storage**: Automatic token caching in `~/.cull-gmail/gmail1/`
//!
//! ## Command Structure
//!
//! ```bash
//! cull-gmail [OPTIONS] [COMMAND]
//! ```
//!
//! ### Global Options
//!
//! - `-v, --verbose...`: Increase logging verbosity (can be used multiple times)
//! - `-q, --quiet...`: Decrease logging verbosity
//! - `-h, --help`: Show help information
//! - `-V, --version`: Show version information
//!
//! ### Commands
//!
//! 1. **`labels`**: List all available Gmail labels
//! 2. **`messages`**: Query and operate on Gmail messages
//! 3. **`rules`**: Configure and execute retention rules
//!
//! ## Configuration File Format
//!
//! The CLI expects a TOML configuration file at `~/.cull-gmail/cull-gmail.toml`:
//!
//! ```toml
//! # OAuth2 credential file (required)
//! credential_file = "client_secret.json"
//!
//! # Configuration root directory
//! config_root = "h:.cull-gmail"
//!
//! # Rules configuration file
//! rules = "rules.toml"
//!
//! # Default execution mode (false = dry-run, true = execute)
//! execute = false
//! ```
//!
//! ## Safety Features
//!
//! - **Dry-run mode**: Default behavior prevents accidental data loss
//! - **Comprehensive logging**: Detailed operation tracking with multiple verbosity levels
//! - **Error handling**: Graceful error recovery with meaningful error messages
//! - **Confirmation prompts**: For destructive operations
//!
//! ## Usage Examples
//!
//! ### List Gmail Labels
//! ```bash
//! cull-gmail labels
//! ```
//!
//! ### Query Messages
//! ```bash
//! # List recent messages
//! cull-gmail messages -m 10 list
//!
//! # Find old promotional emails
//! cull-gmail messages -Q "label:promotions older_than:1y" list
//! ```
//!
//! ### Execute Rules
//! ```bash
//! # Preview rule execution (dry-run)
//! cull-gmail rules run
//!
//! # Execute rules for real
//! cull-gmail rules run --execute
//! ```
//!
//! ## Error Handling
//!
//! The CLI returns the following exit codes:
//! - **0**: Success
//! - **101**: Error (check stderr and logs for details)
//!
//! ## Logging
//!
//! Logging is controlled through command-line verbosity flags and environment variables:
//!
//! - **Default**: Info level logging for the cull-gmail crate
//! - **Verbose (`-v`)**: Debug level logging
//! - **Very Verbose (`-vv`)**: Trace level logging
//! - **Quiet (`-q`)**: Error level logging only
//!
//! Environment variable override:
//! ```bash
//! export RUST_LOG=cull_gmail=debug
//! ```
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
mod labels_cli; mod labels_cli;
@@ -12,28 +123,103 @@ use labels_cli::LabelsCli;
use messages_cli::MessagesCli; use messages_cli::MessagesCli;
use rules_cli::RulesCli; use rules_cli::RulesCli;
/// Main CLI application structure defining global options and subcommands.
///
/// This struct represents the root of the command-line interface, providing
/// global configuration options and dispatching to specific subcommands for
/// labels, messages, and rules management.
///
/// # Global Options
///
/// - **Logging**: Configurable verbosity levels for operation visibility
/// - **Subcommands**: Optional command selection (defaults to rule execution)
///
/// # Default Behavior
///
/// When no subcommand is provided, the CLI executes the default rule processing
/// workflow, loading rules from the configuration file and executing them
/// according to the current execution mode (dry-run or live).
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
struct Cli { struct Cli {
/// Logging verbosity control.
///
/// Use `-q` for quiet (errors only), default for info level,
/// `-v` for debug level, `-vv` for trace level.
#[clap(flatten)] #[clap(flatten)]
logging: clap_verbosity_flag::Verbosity, logging: clap_verbosity_flag::Verbosity,
/// Optional subcommand selection.
///
/// If not provided, the CLI will execute the default rule processing workflow.
#[command(subcommand)] #[command(subcommand)]
sub_command: Option<SubCmds>, sub_command: Option<SubCmds>,
} }
/// Available CLI subcommands for Gmail message management.
///
/// Each subcommand provides specialized functionality for different aspects
/// of Gmail message lifecycle management, from inspection to automated processing.
///
/// # Command Categories
///
/// - **Messages**: Direct message querying, filtering, and batch operations
/// - **Labels**: Gmail label inspection and management
/// - **Rules**: Automated message lifecycle rule configuration and execution
///
/// # Display Order
///
/// Commands are ordered by typical usage workflow: inspect labels first,
/// then query specific messages, and finally configure automated rules.
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum SubCmds { enum SubCmds {
/// List messages /// Query, filter, and perform batch operations on Gmail messages.
///
/// Supports advanced Gmail query syntax, label filtering, and batch actions
/// including trash and permanent deletion with safety controls.
#[clap(name = "messages", display_order = 3, next_help_heading = "Labels")] #[clap(name = "messages", display_order = 3, next_help_heading = "Labels")]
Message(MessagesCli), Message(MessagesCli),
/// List labels
/// List and inspect available Gmail labels.
///
/// Displays all labels in your Gmail account with their internal IDs,
/// useful for understanding label structure before creating queries or rules.
#[clap(name = "labels", display_order = 2, next_help_heading = "Rules")] #[clap(name = "labels", display_order = 2, next_help_heading = "Rules")]
Labels(LabelsCli), Labels(LabelsCli),
/// Configure and run rules
/// Configure and execute automated message retention rules.
///
/// Provides rule-based message lifecycle management with configurable
/// retention periods, label targeting, and automated actions.
#[clap(name = "rules", display_order = 2)] #[clap(name = "rules", display_order = 2)]
Rules(RulesCli), Rules(RulesCli),
} }
/// CLI application entry point with comprehensive error handling and logging setup.
///
/// This function initializes the async runtime, parses command-line arguments,
/// configures logging based on user preferences, and orchestrates the main
/// application workflow with proper error handling and exit code management.
///
/// # Process Flow
///
/// 1. **Argument Parsing**: Parse command-line arguments using clap
/// 2. **Logging Setup**: Initialize logging with user-specified verbosity
/// 3. **Application Execution**: Run the main application logic
/// 4. **Error Handling**: Handle errors with detailed reporting
/// 5. **Exit Code**: Return appropriate exit codes for shell integration
///
/// # Exit Codes
///
/// - **0**: Successful execution
/// - **101**: Error occurred (details logged and printed to stderr)
///
/// # Error Reporting
///
/// Errors are reported through multiple channels:
/// - **Logging**: Structured error logging for debugging
/// - **stderr**: User-friendly error messages
/// - **Exit codes**: Shell-scriptable status reporting
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let args = Cli::parse(); let args = Cli::parse();
@@ -57,6 +243,34 @@ async fn main() {
}); });
} }
/// Main application logic dispatcher handling subcommand execution and default behavior.
///
/// This function orchestrates the core application workflow by:
/// 1. Loading configuration from files and environment
/// 2. Initializing the Gmail API client with OAuth2 authentication
/// 3. Dispatching to appropriate subcommands or executing default rule processing
///
/// # Arguments
///
/// * `args` - Parsed command-line arguments containing global options and subcommands
///
/// # Returns
///
/// Returns `Result<()>` indicating success or failure of the operation.
///
/// # Default Behavior
///
/// When no subcommand is specified, the function executes the default rule processing
/// workflow, loading rules from configuration and executing them based on the
/// current execution mode setting.
///
/// # Error Handling
///
/// Errors can occur during:
/// - Configuration loading and parsing
/// - Gmail client initialization and authentication
/// - Subcommand execution
/// - Rule processing operations
async fn run(args: Cli) -> Result<()> { async fn run(args: Cli) -> Result<()> {
let (config, client_config) = get_config()?; let (config, client_config) = get_config()?;
@@ -75,6 +289,35 @@ async fn run(args: Cli) -> Result<()> {
} }
} }
/// Creates and configures a logging builder with appropriate verbosity levels.
///
/// This function sets up structured logging for the application with:
/// - Minimum info-level logging for user-facing information
/// - Configurable verbosity based on command-line flags
/// - Timestamp formatting for operation tracking
/// - Focused logging on the cull-gmail crate to reduce noise
///
/// # Arguments
///
/// * `level` - Desired log level filter from command-line verbosity flags
///
/// # Returns
///
/// Returns a configured `env_logger::Builder` ready for initialization.
///
/// # Logging Levels
///
/// - **Error**: Critical failures and unrecoverable errors
/// - **Warn**: Non-fatal issues, dry-run notifications, missing resources
/// - **Info**: General operation progress, message counts, rule execution
/// - **Debug**: Detailed operation info, API calls, configuration values
/// - **Trace**: Very detailed debugging information
///
/// # Default Behavior
///
/// The function enforces a minimum of Info-level logging to ensure users
/// receive adequate feedback about application operations, even when
/// verbosity is not explicitly requested.
fn get_logging(level: log::LevelFilter) -> env_logger::Builder { fn get_logging(level: log::LevelFilter) -> env_logger::Builder {
let level = if level > log::LevelFilter::Info { let level = if level > log::LevelFilter::Info {
level level
@@ -92,13 +335,54 @@ fn get_logging(level: log::LevelFilter) -> env_logger::Builder {
builder builder
} }
/// Loads and parses application configuration from multiple sources.
///
/// This function implements a hierarchical configuration loading strategy:
/// 1. **Default values**: Sensible defaults for all configuration options
/// 2. **Configuration file**: User-specific settings from `~/.cull-gmail/cull-gmail.toml`
/// 3. **Environment variables**: Runtime overrides with `APP_` prefix
///
/// # Returns
///
/// Returns a tuple containing:
/// - **Config**: Raw configuration for general application settings
/// - **ClientConfig**: Processed Gmail client configuration with OAuth2 setup
///
/// # Configuration Hierarchy
///
/// Settings are applied in this order (later sources override earlier ones):
/// 1. Built-in defaults
/// 2. Configuration file values
/// 3. Environment variable overrides
///
/// # Configuration Parameters
///
/// ## Default Values:
/// - `credentials`: "credential.json" - OAuth2 credential file name
/// - `config_root`: "h:.cull-gmail" - Configuration directory (home-relative)
/// - `rules`: "rules.toml" - Rules configuration file name
/// - `execute`: true - Default execution mode (can be overridden for safety)
///
/// ## Environment Variables:
/// - `APP_CREDENTIALS`: Override credential file name
/// - `APP_CONFIG_ROOT`: Override configuration directory
/// - `APP_RULES`: Override rules file name
/// - `APP_EXECUTE`: Override execution mode (true/false)
///
/// # Error Handling
///
/// Configuration errors can occur due to:
/// - Missing or inaccessible configuration files
/// - Invalid TOML syntax in configuration files
/// - Missing OAuth2 credential files
/// - Invalid OAuth2 credential format or structure
fn get_config() -> Result<(Config, ClientConfig)> { fn get_config() -> Result<(Config, ClientConfig)> {
let home_dir = env::home_dir().unwrap(); let home_dir = env::home_dir().unwrap();
let path = home_dir.join(".cull-gmail/cull-gmail.toml"); let path = home_dir.join(".cull-gmail/cull-gmail.toml");
log::info!("Loading config from {}", path.display()); log::info!("Loading config from {}", path.display());
let configurations = config::Config::builder() let configurations = config::Config::builder()
.set_default("credentials", "credential.json")? .set_default("credential_file", "credential.json")?
.set_default("config_root", "h:.cull-gmail")? .set_default("config_root", "h:.cull-gmail")?
.set_default("rules", "rules.toml")? .set_default("rules", "rules.toml")?
.set_default("execute", true)? .set_default("execute", true)?
@@ -114,6 +398,41 @@ fn get_config() -> Result<(Config, ClientConfig)> {
)) ))
} }
/// Executes automated message retention rules across Gmail labels.
///
/// This function orchestrates the rule-based message processing workflow by:
/// 1. Organizing rules by their target labels
/// 2. Processing each label according to its configured rule
/// 3. Executing or simulating actions based on execution mode
///
/// # Arguments
///
/// * `client` - Mutable Gmail client for API operations
/// * `rules` - Loaded rules configuration containing all retention policies
/// * `execute` - Whether to actually perform actions (true) or dry-run (false)
///
/// # Returns
///
/// Returns `Result<()>` indicating success or failure of the rule processing.
///
/// # Rule Processing Flow
///
/// For each configured label:
/// 1. **Rule Lookup**: Find the retention rule for the label
/// 2. **Rule Application**: Apply rule criteria to find matching messages
/// 3. **Action Determination**: Determine appropriate action (trash/delete)
/// 4. **Execution**: Execute action or simulate for dry-run
///
/// # Safety Features
///
/// - **Dry-run mode**: When `execute` is false, actions are logged but not performed
/// - **Error isolation**: Errors for individual labels don't stop processing of other labels
/// - **Detailed logging**: Comprehensive logging of rule execution and results
///
/// # Error Handling
///
/// The function continues processing even if individual rules fail, logging
/// warnings for missing rules, processing errors, or action failures.
async fn run_rules(client: &mut GmailClient, rules: Rules, execute: bool) -> Result<()> { async fn run_rules(client: &mut GmailClient, rules: Rules, execute: bool) -> Result<()> {
let rules_by_labels = rules.get_rules_by_label(); let rules_by_labels = rules.get_rules_by_label();
@@ -145,6 +464,41 @@ async fn run_rules(client: &mut GmailClient, rules: Rules, execute: bool) -> Res
Ok(()) Ok(())
} }
/// Executes the specified end-of-life action on messages for a Gmail label.
///
/// This function performs the actual message operations (trash or delete) based on
/// the rule configuration and execution mode. It handles both recoverable (trash)
/// and permanent (delete) operations with appropriate logging and error handling.
///
/// # Arguments
///
/// * `action` - The end-of-life action to perform (Trash or Delete)
/// * `client` - Gmail client configured with messages to process
/// * `label` - Label name for context in logging and error reporting
///
/// # Actions
///
/// ## Trash
/// - **Operation**: Moves messages to Gmail's Trash folder
/// - **Reversibility**: Messages can be recovered from Trash for ~30 days
/// - **Safety**: Relatively safe operation with recovery options
///
/// ## Delete
/// - **Operation**: Permanently deletes messages from Gmail
/// - **Reversibility**: **IRREVERSIBLE** - messages cannot be recovered
/// - **Safety**: High-risk operation requiring careful consideration
///
/// # Error Handling
///
/// The function logs errors but does not propagate them, allowing rule processing
/// to continue for other labels even if one action fails. Errors are reported through:
/// - **Warning logs**: Structured logging for debugging
/// - **Label context**: Error messages include label name for traceability
///
/// # Safety Considerations
///
/// This function should only be called when execute mode is enabled and after
/// appropriate user confirmation for destructive operations.
async fn execute_action(action: EolAction, client: &GmailClient, label: &str) { async fn execute_action(action: EolAction, client: &GmailClient, label: &str) {
match action { match action {
EolAction::Trash => { EolAction::Trash => {