use clap::{CommandFactory, ValueEnum};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum CompressionMethod {
#[default]
Deflated,
Stored,
Bzip2,
Zstd,
}
impl CompressionMethod {
pub fn default_level(self) -> i64 {
match self {
CompressionMethod::Deflated => 6,
CompressionMethod::Stored => 0,
CompressionMethod::Bzip2 => 6,
CompressionMethod::Zstd => 3,
}
}
pub fn level_range(self) -> std::ops::RangeInclusive<i64> {
match self {
CompressionMethod::Deflated => 0..=9,
CompressionMethod::Stored => 0..=0,
CompressionMethod::Bzip2 => 0..=9,
CompressionMethod::Zstd => -7..=22,
}
}
}
impl From<CompressionMethod> for zip::CompressionMethod {
fn from(source: CompressionMethod) -> Self {
match source {
CompressionMethod::Deflated => zip::CompressionMethod::Deflated,
CompressionMethod::Stored => zip::CompressionMethod::Stored,
CompressionMethod::Bzip2 => zip::CompressionMethod::Bzip2,
CompressionMethod::Zstd => zip::CompressionMethod::ZSTD,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize, clap::Parser, Clone, Copy, Eq, PartialEq)]
pub struct CompressionOptions {
#[arg(long, value_enum, default_value_t = CompressionMethod::default())]
pub compression_method: CompressionMethod,
#[arg(long, allow_negative_numbers = true)]
pub compression_level: Option<i64>,
}
impl CompressionOptions {
pub fn validate(&self) {
if let Some(level) = self.compression_level {
let range = self.compression_method.level_range();
if !range.contains(&level) {
let mut cmd = Self::command();
cmd.error(
clap::error::ErrorKind::ArgumentConflict,
format!(
"Invalid level {} for compression method {:?}. Use a level in range {:?}",
level, self.compression_method, range
),
)
.exit();
}
}
}
pub fn none() -> Self {
let method = CompressionMethod::Stored;
Self {
compression_method: method,
compression_level: Some(method.default_level()),
}
}
pub(crate) fn get_file_options(&self) -> zip::write::FileOptions<'static, ()> {
let method = if cfg!(feature = "faster-tests") {
CompressionMethod::Stored
} else {
self.compression_method
};
let mut options =
zip::write::SimpleFileOptions::default().compression_method(method.into());
options = options.compression_level(if method == CompressionMethod::Stored {
None
} else {
Some(self.compression_level.unwrap_or(method.default_level()))
});
options
}
}