use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(name = "smirrors")]
#[command(about = "Automatic mirror list updater for Linux distributions", long_about = None)]
#[command(version)]
#[command(author)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short, long, global = true, value_name = "FILE")]
pub config: Option<PathBuf>,
#[arg(short, long, global = true, conflicts_with = "verbose")]
pub quiet: bool,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
Test {
#[arg(short = 'n', long, value_name = "COUNT")]
count: Option<usize>,
#[arg(short, long, value_enum, default_value = "table")]
format: OutputFormat,
#[arg(short, long)]
success_only: bool,
#[arg(short = 'S', long, value_enum, default_value = "score")]
sort: SortBy,
},
Update {
#[arg(long)]
dry_run: bool,
#[arg(short, long)]
force: bool,
#[arg(short = 'n', long, value_name = "COUNT")]
limit: Option<usize>,
#[arg(short = 'y', long)]
yes: bool,
},
List {
#[arg(long)]
static_only: bool,
#[arg(short = 't', long)]
with_tests: bool,
#[arg(short, long, value_enum, default_value = "table")]
format: OutputFormat,
},
Add {
#[arg(short, long, value_name = "NAME")]
repo: Option<String>,
#[arg(value_name = "URL")]
url: String,
#[arg(long)]
skip_validation: bool,
},
Remove {
#[arg(value_name = "MIRROR")]
mirror: String,
#[arg(short = 'y', long)]
yes: bool,
},
Tui,
Status {
#[arg(short, long)]
detailed: bool,
#[arg(short, long, value_enum, default_value = "pretty")]
format: OutputFormat,
},
History {
#[arg(short = 'n', long, default_value = "10", value_name = "COUNT")]
count: usize,
#[arg(short, long, value_enum, default_value = "table")]
format: OutputFormat,
#[arg(short, long)]
success_only: bool,
#[arg(short = 'F', long, conflicts_with = "success_only")]
failed_only: bool,
},
Rollback {
#[arg(value_name = "BACKUP_ID")]
backup_id: Option<String>,
#[arg(short = 'y', long)]
yes: bool,
#[arg(short, long)]
list: bool,
},
Enable {
#[arg(short, long)]
now: bool,
},
Disable {
#[arg(short, long)]
stop: bool,
},
Config {
#[command(subcommand)]
action: Option<ConfigAction>,
},
Init {
#[arg(short, long)]
force: bool,
#[arg(long)]
skip_service: bool,
},
#[command(hide = true)]
Service {
#[command(subcommand)]
action: ServiceAction,
},
}
#[derive(Subcommand, Debug)]
pub enum ConfigAction {
Show {
#[arg(short, long)]
raw: bool,
#[arg(short, long, value_name = "SECTION")]
section: Option<String>,
},
Set {
#[arg(value_name = "KEY")]
key: String,
#[arg(value_name = "VALUE")]
value: String,
},
Get {
#[arg(value_name = "KEY")]
key: String,
},
Edit {
#[arg(short, long, default_value = "true")]
validate: bool,
},
Validate {
#[arg(short, long)]
verbose: bool,
},
Reset {
#[arg(value_name = "SECTION")]
section: Option<String>,
#[arg(short = 'y', long)]
yes: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum ServiceAction {
Run,
Update,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum OutputFormat {
Table,
Json,
Pretty,
Compact,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SortBy {
Score,
Speed,
Latency,
Url,
}
impl Cli {
pub fn parse_args() -> Self {
Self::parse()
}
pub fn log_level(&self) -> &str {
if self.quiet {
"error"
} else if self.verbose {
"debug"
} else {
"info"
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_parsing_test_command() {
let cli = Cli::parse_from(["smirrors", "test", "--count", "5"]);
match cli.command {
Commands::Test { count, .. } => {
assert_eq!(count, Some(5));
}
_ => panic!("Expected Test command"),
}
}
#[test]
fn test_cli_parsing_update_command() {
let cli = Cli::parse_from(["smirrors", "update", "--dry-run"]);
match cli.command {
Commands::Update { dry_run, .. } => {
assert!(dry_run);
}
_ => panic!("Expected Update command"),
}
}
#[test]
fn test_cli_parsing_config_set() {
let cli = Cli::parse_from(["smirrors", "config", "set", "general.timeout", "15"]);
match cli.command {
Commands::Config {
action: Some(ConfigAction::Set { key, value }),
} => {
assert_eq!(key, "general.timeout");
assert_eq!(value, "15");
}
_ => panic!("Expected Config Set command"),
}
}
#[test]
fn test_log_level_quiet() {
let cli = Cli::parse_from(["smirrors", "--quiet", "status"]);
assert_eq!(cli.log_level(), "error");
}
#[test]
fn test_log_level_verbose() {
let cli = Cli::parse_from(["smirrors", "--verbose", "status"]);
assert_eq!(cli.log_level(), "debug");
}
#[test]
fn test_log_level_default() {
let cli = Cli::parse_from(["smirrors", "status"]);
assert_eq!(cli.log_level(), "info");
}
}