use clap::Args;
use std::ffi::OsStr;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[derive(Args, Debug)]
pub struct CommonArgs {
#[arg(short, long)]
pub input: Option<String>,
#[arg(short, long)]
pub output: Option<String>,
#[arg(short, long)]
pub compress: bool,
#[arg(short, long)]
pub extract: bool,
#[arg(short, long)]
pub decompress: bool,
#[arg()]
pub io_list: Vec<String>,
#[arg(long)]
pub ignore_pipes: bool,
#[arg(long)]
pub ignore_stdin: bool,
#[arg(long)]
pub ignore_stdout: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct CompressionLevel {
pub level: u32,
}
impl Default for CompressionLevel {
fn default() -> Self {
CompressionLevel { level: 6 }
}
}
impl FromStr for CompressionLevel {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(level) = s.parse::<u32>() {
if level < 10 {
return Ok(CompressionLevel { level });
} else {
return Err("Compression level must be 0-9");
}
}
let s = s.to_lowercase();
match s.as_str() {
"none" => Ok(CompressionLevel { level: 0 }),
"fast" => Ok(CompressionLevel { level: 1 }),
"best" => Ok(CompressionLevel { level: 9 }),
_ => Err("Invalid compression level"),
}
}
}
#[derive(Args, Debug, Default, Clone, Copy)]
pub struct LevelArgs {
#[arg(long, default_value = "6")]
pub level: CompressionLevel,
}
#[allow(unused_variables)]
pub trait Compressor {
fn name(&self) -> &str;
fn extension(&self) -> &str {
self.name()
}
fn is_archive(&self, in_path: &Path) -> bool {
if in_path.extension().is_none() {
return false;
}
in_path.extension().unwrap() == self.extension()
}
fn default_compressed_filename(&self, in_path: &Path) -> String {
format!(
"{}.{}",
in_path
.file_name()
.unwrap_or_else(|| OsStr::new("archive"))
.to_str()
.unwrap(),
self.extension()
)
}
fn default_extracted_filename(&self, in_path: &Path) -> String {
if in_path.extension().unwrap() == self.extension() {
return in_path.file_stem().unwrap().to_str().unwrap().to_string();
}
if in_path.extension().is_none() {
return ".".to_string();
}
".".to_string()
}
fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
cmprss_error("compress_target unimplemented")
}
fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
cmprss_error("extract_target unimplemented")
}
}
impl fmt::Debug for dyn Compressor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Compressor {{ name: {} }}", self.name())
}
}
pub fn cmprss_error(message: &str) -> Result<(), io::Error> {
Err(io::Error::new(io::ErrorKind::Other, message))
}
#[derive(Debug)]
pub enum CmprssInput {
Path(Vec<PathBuf>),
Pipe(std::io::Stdin),
}
#[derive(Debug)]
pub enum CmprssOutput {
Path(PathBuf),
Pipe(std::io::Stdout),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compression_level_parsing() {
assert_eq!(CompressionLevel::from_str("0").unwrap().level, 0);
assert_eq!(CompressionLevel::from_str("1").unwrap().level, 1);
assert_eq!(CompressionLevel::from_str("9").unwrap().level, 9);
assert_eq!(CompressionLevel::from_str("none").unwrap().level, 0);
assert_eq!(CompressionLevel::from_str("fast").unwrap().level, 1);
assert_eq!(CompressionLevel::from_str("best").unwrap().level, 9);
assert!(CompressionLevel::from_str("-1").is_err());
assert!(CompressionLevel::from_str("10").is_err());
assert!(CompressionLevel::from_str("foo").is_err());
}
}