use clap::{Parser, ValueHint};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CompressionType {
#[default]
Gzip,
Bzip2,
Xz,
Zstd,
None,
}
impl std::fmt::Display for CompressionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Gzip => write!(f, "gz"),
Self::Bzip2 => write!(f, "bz2"),
Self::Xz => write!(f, "xz"),
Self::Zstd => write!(f, "zst"),
Self::None => write!(f, "none"),
}
}
}
impl std::str::FromStr for CompressionType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"gz" | "gzip" => Ok(Self::Gzip),
"bz2" | "bzip2" => Ok(Self::Bzip2),
"xz" => Ok(Self::Xz),
"zst" | "zstd" => Ok(Self::Zstd),
"none" => Ok(Self::None),
_ => Err(format!("Unknown compression type: {s}")),
}
}
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[arg(value_hint = ValueHint::DirPath)]
pub path: PathBuf,
#[arg(short = 'q', long)]
pub quiet: bool,
#[arg(short = 'v', long)]
pub verbose: bool,
#[arg(short, long, value_hint = ValueHint::DirPath)]
pub outputdir: Option<PathBuf>,
#[arg(short = 'b', long)]
pub baseurl: Option<String>,
#[arg(long, value_hint = ValueHint::DirPath)]
pub basedir: Option<PathBuf>,
#[arg(short = 'x', long)]
pub excludes: Option<String>,
#[arg(long)]
pub skip_symlinks: bool,
#[arg(short = 'p', long, default_value = "true")]
pub pretty: bool,
#[arg(long)]
pub no_pretty: bool,
#[arg(short = 'w', long)]
pub workers: Option<usize>,
#[arg(long, default_value = "gz")]
pub compress_type: String,
#[arg(long, default_value = "6")]
pub compress_level: Option<i32>,
#[arg(long, default_value = "true")]
pub unique_md_filenames: bool,
#[arg(long)]
pub no_database: bool,
#[arg(long)]
pub update: bool,
#[arg(long)]
pub aggressive: bool,
#[arg(long)]
pub revision: Option<String>,
#[arg(long, default_value = "sha256")]
pub checksum: String,
#[arg(long)]
pub distro: Vec<String>,
#[arg(long)]
pub content_tag: Vec<String>,
#[arg(long)]
pub repo_tag: Vec<String>,
#[arg(short = 'g', long)]
pub groupfile: Option<PathBuf>,
#[arg(long)]
pub deltarpm: bool,
#[arg(long)]
pub filelists_ext: bool,
#[arg(long, value_hint = ValueHint::DirPath)]
pub oldpackagedirs: Vec<PathBuf>,
#[arg(long)]
pub num_deltas: Option<usize>,
#[arg(long)]
pub max_delta_rpm_size: Option<u64>,
#[arg(long, value_hint = ValueHint::DirPath)]
pub update_md_path: Option<PathBuf>,
#[arg(long)]
pub skip_stat: bool,
#[arg(long, value_hint = ValueHint::FilePath)]
pub pkglist: Option<PathBuf>,
#[arg(long)]
pub includepkg: Vec<String>,
#[arg(long)]
pub changelog_limit: Option<String>,
#[arg(long)]
pub simple_md_filenames: bool,
#[arg(long)]
pub retain_old_md: bool,
#[arg(long)]
pub set_timestamp_to_revision: bool,
#[arg(long, value_hint = ValueHint::FilePath)]
pub read_pkgs_list: Option<PathBuf>,
#[arg(long)]
pub xz: bool,
#[arg(long)]
pub general_compress_type: Option<String>,
#[arg(long)]
pub keep_all_metadata: bool,
#[arg(long)]
pub discard_additional_metadata: bool,
#[arg(long)]
pub compatibility: bool,
#[arg(long)]
pub retain_old_md_by_age: Option<String>,
#[arg(long, value_hint = ValueHint::DirPath)]
pub cachedir: Option<PathBuf>,
#[arg(long)]
pub local_sqlite: bool,
#[arg(long)]
pub cut_dirs: Option<usize>,
#[arg(long)]
pub location_prefix: Option<String>,
#[arg(long)]
pub repomd_checksum: Option<String>,
#[arg(long)]
pub error_exit_val: bool,
#[arg(long)]
pub recycle_pkglist: bool,
#[arg(long)]
pub duplicated_nevra: Option<String>,
}
impl Cli {
#[must_use]
pub fn parse_args() -> Self {
Self::parse()
}
#[must_use]
pub fn exclude_patterns(&self) -> Vec<String> {
self.excludes
.as_ref()
.map(|s| s.split(',').map(String::from).collect())
.unwrap_or_default()
}
#[must_use]
pub fn compression(&self) -> CompressionType {
self.compress_type.parse().unwrap_or(CompressionType::Gzip)
}
#[must_use]
pub fn workers(&self) -> usize {
self.workers.unwrap_or(num_cpus::get())
}
#[must_use]
pub fn distro_tags(&self) -> Vec<(Option<String>, String)> {
self.distro
.iter()
.map(|s| {
if let Some((cpeid, tag)) = s.split_once(',') {
(Some(cpeid.to_string()), tag.to_string())
} else {
(None, s.clone())
}
})
.collect()
}
#[must_use]
pub fn content_tags(&self) -> Vec<String> {
self.content_tag.clone()
}
#[must_use]
pub fn repo_tags(&self) -> Vec<String> {
self.repo_tag.clone()
}
#[must_use]
pub const fn is_simple_md_filenames(&self) -> bool {
self.simple_md_filenames
}
#[must_use]
pub const fn has_retain_policy(&self) -> bool {
self.retain_old_md || self.retain_old_md_by_age.is_some()
}
#[must_use]
pub const fn has_additional_metadata_policy(&self) -> bool {
self.keep_all_metadata || self.discard_additional_metadata
}
}