- move action execution logic into `execute_action` function - improve code readability and reduce duplication
164 lines
4.8 KiB
Rust
164 lines
4.8 KiB
Rust
use clap::{Parser, Subcommand};
|
|
|
|
mod labels_cli;
|
|
mod messages_cli;
|
|
mod rules_cli;
|
|
|
|
use config::Config;
|
|
use cull_gmail::{ClientConfig, EolAction, GmailClient, Result, RuleProcessor, Rules};
|
|
use std::{env, error::Error as stdError};
|
|
|
|
use labels_cli::LabelsCli;
|
|
use messages_cli::MessagesCli;
|
|
use rules_cli::RulesCli;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[clap(author, version, about, long_about = None)]
|
|
struct Cli {
|
|
#[clap(flatten)]
|
|
logging: clap_verbosity_flag::Verbosity,
|
|
#[command(subcommand)]
|
|
sub_command: Option<SubCmds>,
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
enum SubCmds {
|
|
/// List messages
|
|
#[clap(name = "messages", display_order = 3, next_help_heading = "Labels")]
|
|
Message(MessagesCli),
|
|
/// List labels
|
|
#[clap(name = "labels", display_order = 2, next_help_heading = "Rules")]
|
|
Labels(LabelsCli),
|
|
/// Configure and run rules
|
|
#[clap(name = "rules", display_order = 2)]
|
|
Rules(RulesCli),
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let args = Cli::parse();
|
|
|
|
let mut logging = get_logging(args.logging.log_level_filter());
|
|
logging.init();
|
|
log::info!("Logging started.");
|
|
|
|
std::process::exit(match run(args).await {
|
|
Ok(_) => 0,
|
|
Err(e) => {
|
|
if let Some(src) = e.source() {
|
|
log::error!("{e}: {src}");
|
|
eprintln!("{e}: {src}");
|
|
} else {
|
|
log::error!("{e}");
|
|
eprintln!("{e}");
|
|
}
|
|
101
|
|
}
|
|
});
|
|
}
|
|
|
|
async fn run(args: Cli) -> Result<()> {
|
|
let (config, client_config) = get_config()?;
|
|
|
|
let mut client = GmailClient::new_with_config(client_config).await?;
|
|
|
|
let Some(sub_command) = args.sub_command else {
|
|
let rules = rules_cli::get_rules()?;
|
|
let execute = config.get_bool("execute").unwrap_or(false);
|
|
return run_rules(&mut client, rules, execute).await;
|
|
};
|
|
|
|
match sub_command {
|
|
SubCmds::Message(messages_cli) => messages_cli.run(&mut client).await,
|
|
SubCmds::Labels(labels_cli) => labels_cli.run(client).await,
|
|
SubCmds::Rules(rules_cli) => rules_cli.run(&mut client).await,
|
|
}
|
|
}
|
|
|
|
fn get_logging(level: log::LevelFilter) -> env_logger::Builder {
|
|
let level = if level > log::LevelFilter::Info {
|
|
level
|
|
} else {
|
|
log::LevelFilter::Info
|
|
};
|
|
|
|
let mut builder = env_logger::Builder::new();
|
|
|
|
builder.filter(Some("cull_gmail"), level);
|
|
// TODO: Provide an option to set wider filter allowing all crates to report
|
|
|
|
builder.format_timestamp_secs().format_module_path(false);
|
|
|
|
builder
|
|
}
|
|
|
|
fn get_config() -> Result<(Config, ClientConfig)> {
|
|
let home_dir = env::home_dir().unwrap();
|
|
let path = home_dir.join(".cull-gmail/cull-gmail.toml");
|
|
log::info!("Loading config from {}", path.display());
|
|
|
|
let configurations = config::Config::builder()
|
|
.set_default("credentials", "credential.json")?
|
|
.set_default("config_root", "h:.cull-gmail")?
|
|
.set_default("rules", "rules.toml")?
|
|
.set_default("execute", true)?
|
|
.add_source(config::File::with_name(
|
|
path.to_path_buf().to_str().unwrap(),
|
|
))
|
|
.add_source(config::Environment::with_prefix("APP"))
|
|
.build()?;
|
|
|
|
Ok((
|
|
configurations.clone(),
|
|
ClientConfig::new_from_configuration(configurations)?,
|
|
))
|
|
}
|
|
|
|
async fn run_rules(client: &mut GmailClient, rules: Rules, execute: bool) -> Result<()> {
|
|
let rules_by_labels = rules.get_rules_by_label();
|
|
|
|
for label in rules.labels() {
|
|
let Some(rule) = rules_by_labels.get(&label) else {
|
|
log::warn!("no rule found for label `{label}`");
|
|
continue;
|
|
};
|
|
|
|
log::info!("Executing rule `#{}` for label `{label}`", rule.describe());
|
|
client.set_rule(rule.clone());
|
|
client.set_execute(execute);
|
|
if let Err(e) = client.find_rule_and_messages_for_label(&label).await {
|
|
log::warn!("Nothing to process for label `{label}` as {e}");
|
|
continue;
|
|
}
|
|
let Some(action) = client.action() else {
|
|
log::warn!("no valid action specified for rule #{}", rule.id());
|
|
continue;
|
|
};
|
|
|
|
if execute {
|
|
execute_action(action, client, &label).await;
|
|
} else {
|
|
log::warn!("Execution stopped for dry run");
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn execute_action(action: EolAction, client: &GmailClient, label: &str) {
|
|
match action {
|
|
EolAction::Trash => {
|
|
log::info!("***executing trash messages***");
|
|
if client.batch_trash().await.is_err() {
|
|
log::warn!("Move to trash failed for label `{label}`");
|
|
}
|
|
}
|
|
EolAction::Delete => {
|
|
log::info!("***executing final delete messages***");
|
|
if client.batch_delete().await.is_err() {
|
|
log::warn!("Delete failed for label `{label}`");
|
|
}
|
|
}
|
|
}
|
|
}
|