use-tar 0.1.0

Tar entry labels and typeflag mappings for RustUse
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

//! Tar entry labels and typeflag mappings for `RustUse`.

use core::fmt;

/// Common tar file extension.
pub const TAR_EXTENSION: &str = "tar";
/// Common gzip-compressed tar extension.
pub const TAR_GZIP_EXTENSION: &str = "tar.gz";
/// Common xz-compressed tar extension.
pub const TAR_XZ_EXTENSION: &str = "tar.xz";
/// Common zstd-compressed tar extension.
pub const TAR_ZSTD_EXTENSION: &str = "tar.zst";

/// Tar entry type labels.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum TarEntryType {
    /// Regular file entry.
    #[default]
    Regular,
    /// Directory entry.
    Directory,
    /// Symbolic link entry.
    Symlink,
    /// Hard link entry.
    Hardlink,
    /// Block device entry.
    BlockDevice,
    /// Character device entry.
    CharacterDevice,
    /// FIFO entry.
    Fifo,
    /// Global extended header entry.
    GlobalExtendedHeader,
    /// Extended header entry.
    ExtendedHeader,
    /// Unknown or unsupported tar entry type.
    Unknown,
}

impl TarEntryType {
    /// Returns a stable lowercase label.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Regular => "regular",
            Self::Directory => "directory",
            Self::Symlink => "symlink",
            Self::Hardlink => "hardlink",
            Self::BlockDevice => "block-device",
            Self::CharacterDevice => "character-device",
            Self::Fifo => "fifo",
            Self::GlobalExtendedHeader => "global-extended-header",
            Self::ExtendedHeader => "extended-header",
            Self::Unknown => "unknown",
        }
    }

    /// Maps a raw tar typeflag byte to a label.
    #[must_use]
    pub const fn from_typeflag(typeflag: u8) -> Self {
        match typeflag {
            0 | b'0' => Self::Regular,
            b'1' => Self::Hardlink,
            b'2' => Self::Symlink,
            b'3' => Self::CharacterDevice,
            b'4' => Self::BlockDevice,
            b'5' => Self::Directory,
            b'6' => Self::Fifo,
            b'g' => Self::GlobalExtendedHeader,
            b'x' => Self::ExtendedHeader,
            _ => Self::Unknown,
        }
    }

    /// Returns the standard tar typeflag byte when one is available.
    #[must_use]
    pub const fn typeflag(self) -> Option<u8> {
        match self {
            Self::Regular => Some(b'0'),
            Self::Hardlink => Some(b'1'),
            Self::Symlink => Some(b'2'),
            Self::CharacterDevice => Some(b'3'),
            Self::BlockDevice => Some(b'4'),
            Self::Directory => Some(b'5'),
            Self::Fifo => Some(b'6'),
            Self::GlobalExtendedHeader => Some(b'g'),
            Self::ExtendedHeader => Some(b'x'),
            Self::Unknown => None,
        }
    }
}

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

#[cfg(test)]
mod tests {
    use super::{TAR_EXTENSION, TAR_GZIP_EXTENSION, TarEntryType};

    #[test]
    fn maps_tar_typeflags() {
        assert_eq!(TarEntryType::from_typeflag(b'5'), TarEntryType::Directory);
        assert_eq!(TarEntryType::from_typeflag(b'2'), TarEntryType::Symlink);
        assert_eq!(TarEntryType::Regular.typeflag(), Some(b'0'));
        assert_eq!(TarEntryType::Unknown.typeflag(), None);
    }

    #[test]
    fn exposes_tar_extension_labels() {
        assert_eq!(TAR_EXTENSION, "tar");
        assert_eq!(TAR_GZIP_EXTENSION, "tar.gz");
    }
}