use clap::{Args, Parser, Subcommand, ValueEnum};
use rars::ArchiveVersion;
#[derive(Parser)]
#[command(
name = "rars",
version,
about = "Pure-Rust RAR archive toolkit",
long_about = "rars reads, writes, and repairs RAR archives across the RAR 1.3 through RAR 7.x family.",
after_help = "Exit codes:\n \
0 success\n \
1 operation failed\n \
2 invalid command line\n \
3 password required, wrong password, or corrupt encrypted data",
propagate_version = true,
disable_help_subcommand = true
)]
pub(crate) struct Cli {
#[arg(long, value_name = "N", global = true, value_parser = crate::parse_thread_count)]
pub threads: Option<usize>,
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub(crate) enum Command {
Info(InfoArgs),
Test(TestArgs),
#[command(visible_alias = "x")]
Extract(ExtractArgs),
Repair(RepairArgs),
#[command(visible_alias = "a")]
Add(AddArgs),
}
#[derive(Args)]
pub(crate) struct PasswordArgs {
#[arg(short = 'p', long, value_name = "PASSWORD")]
pub password: Option<String>,
#[arg(long, value_name = "PATH", conflicts_with = "password")]
pub password_file: Option<String>,
}
#[derive(Args)]
pub(crate) struct ReadOptionsArgs {
#[arg(long, value_name = "SIZE", value_parser = crate::parse_size_string)]
pub rar50_buffered_decode_limit: Option<usize>,
}
#[derive(Args)]
pub(crate) struct InfoArgs {
#[command(flatten)]
pub password: PasswordArgs,
#[arg(short = 'v', long)]
pub verbose: bool,
#[arg(value_name = "ARCHIVE", required = true)]
pub paths: Vec<String>,
}
#[derive(Args)]
pub(crate) struct TestArgs {
#[command(flatten)]
pub password: PasswordArgs,
#[command(flatten)]
pub read_options: ReadOptionsArgs,
#[arg(value_name = "ARCHIVE", required = true)]
pub paths: Vec<String>,
}
#[derive(Args)]
pub(crate) struct ExtractArgs {
#[command(flatten)]
pub password: PasswordArgs,
#[command(flatten)]
pub read_options: ReadOptionsArgs,
#[arg(long, value_enum, default_value_t = OverwriteCli::Never)]
pub overwrite: OverwriteCli,
#[arg(value_name = "PATH", required = true, num_args = 2..)]
pub paths: Vec<String>,
}
#[derive(Args)]
pub(crate) struct RepairArgs {
#[command(flatten)]
pub password: PasswordArgs,
#[arg(value_name = "PATH", required = true, num_args = 2..)]
pub paths: Vec<String>,
}
#[derive(Args)]
pub(crate) struct AddArgs {
#[command(flatten)]
pub password: PasswordArgs,
#[arg(long, value_enum, default_value_t = TargetFormat::Rar50)]
pub format: TargetFormat,
#[arg(long)]
pub store: bool,
#[arg(long, value_name = "LEVEL")]
pub level: Option<u8>,
#[arg(long, value_name = "SIZE", value_parser = crate::parse_size_string)]
pub dict_size: Option<usize>,
#[arg(long)]
pub solid: bool,
#[arg(long = "encrypt-headers")]
pub encrypt_headers: bool,
#[arg(long = "quick-open")]
pub quick_open: bool,
#[arg(long, value_name = "TEXT")]
pub comment: Option<String>,
#[arg(long = "archive-name", value_name = "NAME")]
pub archive_name: Option<String>,
#[arg(long = "file-comment", value_name = "TEXT")]
pub file_comment: Option<String>,
#[arg(long = "recovery-percent", value_name = "PERCENT")]
pub recovery_percent: Option<u64>,
#[arg(long = "volume-size", value_name = "SIZE", value_parser = crate::parse_size_string)]
pub volume_size: Option<usize>,
#[arg(long = "delta-filter", value_name = "CHANNELS")]
pub delta_filter: Option<usize>,
#[arg(long = "e8-filter", conflicts_with = "e8e9_filter")]
pub e8_filter: bool,
#[arg(long = "e8e9-filter")]
pub e8e9_filter: bool,
#[arg(long = "itanium-filter")]
pub itanium_filter: bool,
#[arg(long = "rgb-filter", value_name = "WIDTH")]
pub rgb_filter: Option<usize>,
#[arg(long = "audio-filter", value_name = "CHANNELS")]
pub audio_filter: Option<usize>,
#[arg(long = "arm-filter")]
pub arm_filter: bool,
#[arg(long = "auto-filter")]
pub auto_filter: bool,
#[arg(long)]
pub ppmd: bool,
#[arg(value_name = "ARCHIVE")]
pub archive: String,
#[arg(value_name = "FILE", required = true)]
pub files: Vec<String>,
}
#[derive(Copy, Clone, ValueEnum)]
pub(crate) enum TargetFormat {
Rar14,
Rar15,
Rar20,
Rar29,
Rar30,
Rar40,
Rar50,
Rar70,
}
impl TargetFormat {
pub(crate) fn archive_version(self) -> ArchiveVersion {
match self {
Self::Rar14 => ArchiveVersion::Rar14,
Self::Rar15 => ArchiveVersion::Rar15,
Self::Rar20 => ArchiveVersion::Rar20,
Self::Rar29 => ArchiveVersion::Rar29,
Self::Rar30 => ArchiveVersion::Rar30,
Self::Rar40 => ArchiveVersion::Rar40,
Self::Rar50 => ArchiveVersion::Rar50,
Self::Rar70 => ArchiveVersion::Rar70,
}
}
}
#[derive(Copy, Clone, ValueEnum)]
pub(crate) enum OverwriteCli {
Never,
Always,
}
impl From<OverwriteCli> for crate::output::OverwritePolicy {
fn from(value: OverwriteCli) -> Self {
match value {
OverwriteCli::Never => Self::Never,
OverwriteCli::Always => Self::Always,
}
}
}
pub(crate) fn parse() -> Cli {
match Cli::try_parse() {
Ok(cli) => cli,
Err(err) => {
use clap::error::ErrorKind;
let message = err.to_string();
match err.kind() {
ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
print!("{message}");
std::process::exit(0);
}
_ => {
eprint!("{message}");
std::process::exit(2);
}
}
}
}
}