use clap::{Parser, Subcommand};
use clap_complete::Shell;
pub fn should_color(choice: ColorChoice, is_tty: bool) -> bool {
match choice {
ColorChoice::Always => true,
ColorChoice::Never => false,
ColorChoice::Auto => is_tty,
}
}
#[cfg(test)]
mod cli_def_tests {
use super::*;
#[test]
fn test_should_color_always() {
assert!(should_color(ColorChoice::Always, false));
assert!(should_color(ColorChoice::Always, true));
}
#[test]
fn test_should_color_never() {
assert!(!should_color(ColorChoice::Never, false));
assert!(!should_color(ColorChoice::Never, true));
}
#[test]
fn test_should_color_auto_tty() {
assert!(should_color(ColorChoice::Auto, true));
}
#[test]
fn test_should_color_auto_no_tty() {
assert!(!should_color(ColorChoice::Auto, false));
}
#[test]
fn test_no_color_env_logic() {
let no_color_set = true; let effective_choice = if no_color_set {
ColorChoice::Never
} else {
ColorChoice::Auto
};
assert!(!should_color(effective_choice, true));
}
}
#[derive(Parser)]
#[command(name = "rusmes")]
#[command(about = "RusMES - Rust Mail Enterprise Server", long_about = None)]
#[command(version)]
pub struct CliApp {
#[arg(long, env = "RUSMES_SERVER", default_value = "http://localhost:8080")]
pub server: String,
#[arg(long, default_value = "./data")]
pub runtime_dir: String,
#[arg(long, value_enum, default_value = "auto")]
pub color: ColorChoice,
#[arg(long)]
pub json: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ColorChoice {
Auto,
Always,
Never,
}
#[derive(Subcommand)]
pub enum Commands {
Init {
#[arg(long)]
domain: String,
},
CheckConfig {
#[arg(short, long, default_value = "rusmes.toml")]
config: String,
},
Status {
#[arg(long, value_name = "INTERVAL_SECS")]
watch: Option<u64>,
},
User {
#[command(subcommand)]
action: UserAction,
},
Mailbox {
#[command(subcommand)]
action: MailboxAction,
},
Queue {
#[command(subcommand)]
action: QueueAction,
},
Backup {
#[command(subcommand)]
action: BackupAction,
},
Restore {
#[command(subcommand)]
action: RestoreAction,
},
Migrate {
#[arg(long)]
from: String,
#[arg(long)]
to: String,
#[arg(long)]
source_config: Option<String>,
#[arg(long)]
dest_config: Option<String>,
#[arg(long, default_value = "100")]
batch_size: usize,
#[arg(long, default_value = "4")]
parallel: usize,
#[arg(long)]
verify: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
resume: bool,
},
Completions {
#[arg(value_enum)]
shell: Shell,
},
Man,
}
#[derive(Subcommand)]
pub enum UserAction {
Add {
email: String,
#[arg(long)]
password: String,
#[arg(long)]
quota: Option<u64>,
},
List,
Delete {
email: String,
#[arg(long)]
force: bool,
},
Passwd {
email: String,
#[arg(long)]
password: String,
},
Show {
email: String,
},
SetQuota {
email: String,
#[arg(long)]
quota: u64,
},
Enable {
email: String,
},
Disable {
email: String,
},
}
#[derive(Subcommand)]
pub enum MailboxAction {
List {
user: String,
},
Create {
user: String,
#[arg(long)]
name: String,
},
Delete {
user: String,
#[arg(long)]
name: String,
#[arg(long)]
force: bool,
},
Rename {
user: String,
#[arg(long)]
old_name: String,
#[arg(long)]
new_name: String,
},
Repair {
#[arg(long)]
mailbox: Option<String>,
#[arg(long)]
vacuum: bool,
},
Subscribe {
user: String,
#[arg(long)]
name: String,
},
Unsubscribe {
user: String,
#[arg(long)]
name: String,
},
Show {
user: String,
#[arg(long)]
name: String,
},
}
#[derive(Subcommand)]
pub enum QueueAction {
List {
#[arg(long)]
filter: Option<String>,
},
Flush,
Inspect {
message_id: String,
},
Delete {
message_id: String,
},
Retry {
message_id: String,
},
Purge,
Stats,
}
#[derive(Subcommand)]
pub enum BackupAction {
Full {
#[arg(short, long)]
output: String,
#[arg(long, value_enum, default_value = "tar-gz")]
format: BackupFormat,
#[arg(long, value_enum, default_value = "gzip")]
compression: CompressionType,
#[arg(long)]
encrypt: bool,
},
Incremental {
#[arg(short, long)]
output: String,
#[arg(long)]
base: String,
#[arg(long, value_enum, default_value = "tar-gz")]
format: BackupFormat,
#[arg(long, value_enum, default_value = "gzip")]
compression: CompressionType,
#[arg(long)]
encrypt: bool,
},
List,
Verify {
backup: String,
#[arg(long)]
key: Option<String>,
},
UploadS3 {
backup: String,
#[arg(long)]
bucket: String,
#[arg(long)]
region: String,
#[arg(long, env = "AWS_ACCESS_KEY_ID")]
access_key: String,
#[arg(long, env = "AWS_SECRET_ACCESS_KEY")]
secret_key: String,
},
}
#[derive(Subcommand)]
pub enum RestoreAction {
Restore {
backup: String,
#[arg(long)]
key: Option<String>,
#[arg(long)]
point_in_time: Option<String>,
#[arg(long)]
dry_run: bool,
},
User {
backup: String,
#[arg(long)]
user: String,
#[arg(long)]
key: Option<String>,
#[arg(long)]
dry_run: bool,
},
FromS3 {
s3_url: String,
#[arg(long)]
bucket: String,
#[arg(long)]
region: String,
#[arg(long, env = "AWS_ACCESS_KEY_ID")]
access_key: String,
#[arg(long, env = "AWS_SECRET_ACCESS_KEY")]
secret_key: String,
#[arg(long)]
key: Option<String>,
},
History,
Show {
restore_id: String,
},
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum BackupFormat {
TarGz,
Binary,
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum CompressionType {
None,
Gzip,
Zstd,
}
impl From<BackupFormat> for crate::commands::backup::BackupFormat {
fn from(f: BackupFormat) -> Self {
match f {
BackupFormat::TarGz => crate::commands::backup::BackupFormat::TarGz,
BackupFormat::Binary => crate::commands::backup::BackupFormat::Binary,
}
}
}
impl From<CompressionType> for crate::commands::backup::CompressionType {
fn from(c: CompressionType) -> Self {
match c {
CompressionType::None => crate::commands::backup::CompressionType::None,
CompressionType::Gzip => crate::commands::backup::CompressionType::Gzip,
CompressionType::Zstd => crate::commands::backup::CompressionType::Zstd,
}
}
}