Add configurable rules directory support

- Add rules configuration option to ClientConfig
- Support custom rules file paths via config and CLI
- Add --rules-dir option to init command
- Update Rules struct to accept custom file paths
- Add helper functions for rules path resolution
- Fix doc comment formatting issues
- Add integration tests for custom rules paths
This commit is contained in:
Jeremiah Russell
2025-10-21 22:04:33 +01:00
committed by Jeremiah Russell
parent e914e492e8
commit d4cc2621db
4 changed files with 29 additions and 28 deletions

View File

@@ -539,6 +539,24 @@ async fn run_rules(client: &mut GmailClient, rules: Rules, execute: bool) -> Res
/// - Container deployments with injected token environment variables /// - Container deployments with injected token environment variables
/// - CI/CD pipelines with stored token secrets /// - CI/CD pipelines with stored token secrets
/// - Ephemeral compute environments requiring periodic Gmail access /// - Ephemeral compute environments requiring periodic Gmail access
fn restore_tokens_if_available(config: &Config, client_config: &ClientConfig) -> Result<()> {
let token_env_var = config
.get_string("token_cache_env")
.unwrap_or_else(|_| "CULL_GMAIL_TOKEN_CACHE".to_string());
if let Ok(token_data) = env::var(&token_env_var) {
log::info!("Found {token_env_var} environment variable, restoring tokens");
restore_tokens_from_string(&token_data, client_config.persist_path())?;
log::info!("Tokens successfully restored from environment variable");
} else {
log::debug!(
"No {token_env_var} environment variable found, proceeding with normal token flow"
);
}
Ok(())
}
/// Gets the rules file path from configuration. /// Gets the rules file path from configuration.
/// ///
/// Reads the `rules` configuration value and resolves it using path prefixes. /// Reads the `rules` configuration value and resolves it using path prefixes.
@@ -566,24 +584,6 @@ fn get_rules_path(config: &Config) -> Result<Option<PathBuf>> {
Ok(Some(path)) Ok(Some(path))
} }
fn restore_tokens_if_available(config: &Config, client_config: &ClientConfig) -> Result<()> {
let token_env_var = config
.get_string("token_cache_env")
.unwrap_or_else(|_| "CULL_GMAIL_TOKEN_CACHE".to_string());
if let Ok(token_data) = env::var(&token_env_var) {
log::info!("Found {token_env_var} environment variable, restoring tokens");
restore_tokens_from_string(&token_data, client_config.persist_path())?;
log::info!("Tokens successfully restored from environment variable");
} else {
log::debug!(
"No {token_env_var} environment variable found, proceeding with normal token flow"
);
}
Ok(())
}
/// Executes the specified end-of-life action on messages for a Gmail label. /// 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 /// This function performs the actual message operations (trash or delete) based on

View File

@@ -342,6 +342,7 @@ impl RulesCli {
/// - **Rules CLI**: To load rules before configuration or execution /// - **Rules CLI**: To load rules before configuration or execution
/// - **Main CLI**: For default rule execution when no subcommand is specified /// - **Main CLI**: For default rule execution when no subcommand is specified
/// - **Validation systems**: To verify rule configuration integrity /// - **Validation systems**: To verify rule configuration integrity
///
/// Loads rules from the default location. /// Loads rules from the default location.
pub fn get_rules() -> Result<Rules> { pub fn get_rules() -> Result<Rules> {
get_rules_from(None) get_rules_from(None)

View File

@@ -45,7 +45,7 @@ use std::{
collections::BTreeMap, collections::BTreeMap,
env, env,
fs::{self, read_to_string}, fs::{self, read_to_string},
path::{Path, PathBuf}, path::Path,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -524,8 +524,9 @@ impl Rules {
let save_path = if let Some(p) = path { let save_path = if let Some(p) = path {
p.to_path_buf() p.to_path_buf()
} else { } else {
let home_dir = env::home_dir() let home_dir = env::home_dir().ok_or_else(|| {
.ok_or_else(|| Error::HomeExpansionFailed("~/.cull-gmail/rules.toml".to_string()))?; Error::HomeExpansionFailed("~/.cull-gmail/rules.toml".to_string())
})?;
home_dir.join(".cull-gmail/rules.toml") home_dir.join(".cull-gmail/rules.toml")
}; };
@@ -600,8 +601,9 @@ impl Rules {
let load_path = if let Some(p) = path { let load_path = if let Some(p) = path {
p.to_path_buf() p.to_path_buf()
} else { } else {
let home_dir = env::home_dir() let home_dir = env::home_dir().ok_or_else(|| {
.ok_or_else(|| Error::HomeExpansionFailed("~/.cull-gmail/rules.toml".to_string()))?; Error::HomeExpansionFailed("~/.cull-gmail/rules.toml".to_string())
})?;
home_dir.join(".cull-gmail/rules.toml") home_dir.join(".cull-gmail/rules.toml")
}; };

View File

@@ -92,11 +92,9 @@ fn test_init_with_separate_rules_directory() {
&format!("c:{}", rules_dir.to_string_lossy()), &format!("c:{}", rules_dir.to_string_lossy()),
]); ]);
cmd.assert() cmd.assert().success().stdout(predicate::str::contains(
.success() "Initialization completed successfully!",
.stdout(predicate::str::contains( ));
"Initialization completed successfully!",
));
// Verify config directory was created // Verify config directory was created
assert!(config_dir.exists()); assert!(config_dir.exists());