cmprss 0.4.0

A compression multi-tool for the command line.
mod brotli;
mod bzip2;
mod containers;
mod gzip;
mod lz4;
mod lzma;
mod pipeline;
mod sevenz;
mod snappy;
mod stream;
mod tar;
mod xz;
mod zip;
mod zstd;

pub use brotli::{Brotli, BrotliArgs};
pub use bzip2::{Bzip2, Bzip2Args};
pub use gzip::{Gzip, GzipArgs};
pub use lz4::{Lz4, Lz4Args};
pub use lzma::{Lzma, LzmaArgs};
pub use pipeline::Pipeline;
pub use sevenz::{SevenZ, SevenZArgs};
pub use snappy::{Snappy, SnappyArgs};
pub use tar::{Tar, TarArgs};
pub use xz::{Xz, XzArgs};
pub use zip::{Zip, ZipArgs};
pub use zstd::{Zstd, ZstdArgs};

use crate::utils::Compressor;

/// Create a default compressor instance from an extension or name string.
/// This is the single canonical lookup table for all compressor types.
pub fn compressor_from_str(s: &str) -> Option<Box<dyn Compressor>> {
    match s {
        "tar" => Some(Box::<Tar>::default()),
        "gzip" | "gz" => Some(Box::<Gzip>::default()),
        "xz" => Some(Box::<Xz>::default()),
        "bzip2" | "bz2" => Some(Box::<Bzip2>::default()),
        "zip" => Some(Box::<Zip>::default()),
        "zstd" | "zst" => Some(Box::<Zstd>::default()),
        "lz4" => Some(Box::<Lz4>::default()),
        "brotli" | "br" => Some(Box::<Brotli>::default()),
        "snappy" | "sz" => Some(Box::<Snappy>::default()),
        "lzma" => Some(Box::<Lzma>::default()),
        "7z" | "sevenz" => Some(Box::<SevenZ>::default()),
        _ => None,
    }
}

/// Resolve an extension to a compressor chain in innermost→outermost order.
/// Single-codec extensions (`gz`, `xz`, `tar`, …) produce a one-element chain;
/// compound shortcut extensions (`tgz`, `tbz`, `tbz2`, `txz`, `tzst`) expand
/// into the chain they represent (e.g. `tgz` → `[tar, gz]`).
///
/// This is the single source of truth for what any archive-like extension
/// means. Both single extensions and compound shortcuts flow through here.
pub fn chain_from_ext(ext: &str) -> Option<Vec<Box<dyn Compressor>>> {
    match ext {
        "tgz" => Some(vec![Box::<Tar>::default(), Box::<Gzip>::default()]),
        "tbz" | "tbz2" => Some(vec![Box::<Tar>::default(), Box::<Bzip2>::default()]),
        "txz" => Some(vec![Box::<Tar>::default(), Box::<Xz>::default()]),
        "tzst" => Some(vec![Box::<Tar>::default(), Box::<Zstd>::default()]),
        _ => compressor_from_str(ext).map(|c| vec![c]),
    }
}

/// Resolve a dotted format string (e.g. `tar.gz`, `tgz`, `xz`) into a
/// compressor chain. Every dot-separated segment is resolved via
/// `chain_from_ext` and concatenated in order. Returns `None` if any
/// segment isn't a known codec or shortcut.
pub fn chain_from_format_str(s: &str) -> Option<Vec<Box<dyn Compressor>>> {
    let mut chain = Vec::new();
    for part in s.split('.') {
        chain.extend(chain_from_ext(part)?);
    }
    if chain.is_empty() { None } else { Some(chain) }
}