use std::ffi::OsString;
use clap::{Args, Parser, Subcommand, ValueEnum};
#[derive(Clone, Debug, Parser)]
#[command(author = "The Tor Project Developers")]
#[command(version)]
pub(crate) struct Cli {
#[command(subcommand)]
pub(crate) command: Commands,
#[arg(long, short, global = true)]
#[arg(value_name = "LEVEL")]
#[clap(default_value_t = LogLevel::Info)]
pub(crate) log_level: LogLevel,
#[arg(long, global = true)]
pub(crate) disable_fs_permission_checks: bool,
#[arg(long = "option", short, global = true)]
#[arg(value_name = "KEY=VALUE")]
pub(crate) options: Vec<String>,
#[arg(long, short, global = true)]
#[arg(value_name = "FILE")]
#[clap(default_values_t = default_config_files().into_iter().map(CliOsString))]
pub(crate) config: Vec<CliOsString>,
}
#[derive(Clone, Debug, Subcommand)]
pub(crate) enum Commands {
Run(RunArgs),
BuildInfo,
}
#[derive(Clone, Debug, Args)]
pub(crate) struct RunArgs {}
fn default_config_files() -> Vec<OsString> {
vec![
"~/.config/arti-relay/arti-relay.toml".into(),
"~/.config/arti-relay/arti-relay.d/".into(),
]
}
#[derive(Clone, Debug, Eq, PartialEq, ValueEnum)]
pub(crate) enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Error => write!(f, "error"),
Self::Warn => write!(f, "warn"),
Self::Info => write!(f, "info"),
Self::Debug => write!(f, "debug"),
Self::Trace => write!(f, "trace"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, derive_more::From)]
pub(crate) struct CliOsString(pub(crate) OsString);
impl std::fmt::Display for CliOsString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.to_string_lossy().fmt(f)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn common_flags() {
Cli::parse_from(["arti-relay", "build-info"]);
Cli::parse_from(["arti-relay", "run"]);
let cli = Cli::parse_from(["arti-relay", "--log-level", "warn", "run"]);
assert_eq!(cli.log_level, LogLevel::Warn);
let cli = Cli::parse_from(["arti-relay", "run", "--log-level", "warn"]);
assert_eq!(cli.log_level, LogLevel::Warn);
let cli = Cli::parse_from(["arti-relay", "--disable-fs-permission-checks", "run"]);
assert!(cli.disable_fs_permission_checks);
let cli = Cli::parse_from(["arti-relay", "run", "--disable-fs-permission-checks"]);
assert!(cli.disable_fs_permission_checks);
}
#[test]
fn clap_bug() {
let cli = Cli::parse_from(["arti-relay", "-o", "foo=1", "run"]);
assert_eq!(cli.options, vec!["foo=1"]);
let cli = Cli::parse_from(["arti-relay", "-o", "foo=1", "-o", "bar=2", "run"]);
assert_eq!(cli.options, vec!["foo=1", "bar=2"]);
let cli = Cli::parse_from(["arti-relay", "-o", "foo=1", "run", "-o", "bar=2"]);
assert_eq!(cli.options, vec!["bar=2"]);
}
}