use-cab 0.1.0

Microsoft Cabinet archive labels, extensions, and compression metadata for RustUse
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

//! Microsoft Cabinet archive labels and compression metadata for `RustUse`.

use core::fmt;

/// Common Microsoft Cabinet archive extension.
pub const CAB_EXTENSION: &str = "cab";
/// Common CAB-related extensions.
pub const CAB_EXTENSIONS: &[&str] = &["cab"];

/// Microsoft Cabinet format labels.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum CabFormat {
    /// Cabinet archive label.
    Cabinet,
    /// Unknown or intentionally unspecified CAB format.
    #[default]
    Unknown,
}

impl CabFormat {
    /// Returns a stable lowercase label.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Cabinet => "cabinet",
            Self::Unknown => "unknown",
        }
    }
}

impl fmt::Display for CabFormat {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.as_str())
    }
}

/// CAB compression method labels.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum CabCompressionMethod {
    /// No compression.
    None,
    /// MSZIP compression.
    MsZip,
    /// Quantum compression.
    Quantum,
    /// LZX compression.
    Lzx,
    /// Unknown or unsupported compression method.
    #[default]
    Unknown,
}

impl CabCompressionMethod {
    /// Returns a stable lowercase label.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::None => "none",
            Self::MsZip => "mszip",
            Self::Quantum => "quantum",
            Self::Lzx => "lzx",
            Self::Unknown => "unknown",
        }
    }
}

impl fmt::Display for CabCompressionMethod {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.as_str())
    }
}

/// Returns whether `extension` is a known CAB extension label.
#[must_use]
pub fn is_cab_extension(extension: &str) -> bool {
    normalize_extension(extension) == "cab"
}

/// Returns whether `name` has a known CAB filename encoding.
#[must_use]
pub fn is_cab_filename(name: &str) -> bool {
    let parts = filename_parts(name);

    matches!(parts.as_slice(), [.., last] if last == "cab")
}

fn normalize_extension(extension: &str) -> String {
    extension
        .trim()
        .trim_start_matches('.')
        .to_ascii_lowercase()
}

fn filename_parts(name: &str) -> Vec<String> {
    name.trim()
        .to_ascii_lowercase()
        .rsplit(['/', '\\'])
        .next()
        .unwrap_or_default()
        .trim_start_matches('.')
        .split('.')
        .filter(|part| !part.is_empty())
        .map(str::to_owned)
        .collect()
}

#[cfg(test)]
mod tests {
    use super::{
        CAB_EXTENSIONS, CabCompressionMethod, CabFormat, is_cab_extension, is_cab_filename,
    };

    #[test]
    fn detects_cab_extensions() {
        assert!(is_cab_extension(".cab"));
        assert_eq!(CAB_EXTENSIONS[0], "cab");
    }

    #[test]
    fn detects_cab_filenames() {
        assert!(is_cab_filename("driver.cab"));
        assert!(is_cab_filename("DRIVER.CAB"));
        assert!(!is_cab_filename("bundle.zip"));
    }

    #[test]
    fn exposes_default_and_unknown_labels() {
        assert_eq!(CabFormat::default(), CabFormat::Unknown);
        assert_eq!(CabFormat::Cabinet.as_str(), "cabinet");
        assert_eq!(
            CabCompressionMethod::default(),
            CabCompressionMethod::Unknown
        );
        assert_eq!(CabCompressionMethod::MsZip.as_str(), "mszip");
    }
}