#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::fmt;
pub const LZ4_EXTENSION: &str = "lz4";
pub const TAR_LZ4_EXTENSION: &str = "tar.lz4";
pub const LZ4_EXTENSIONS: &[&str] = &["lz4", "tar.lz4"];
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Lz4Extension {
Lz4,
TarLz4,
#[default]
Unknown,
}
impl Lz4Extension {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Lz4 => "lz4",
Self::TarLz4 => "tar.lz4",
Self::Unknown => "unknown",
}
}
#[must_use]
pub fn from_extension(extension: &str) -> Self {
match normalize_extension(extension).as_str() {
"lz4" => Self::Lz4,
"tar.lz4" => Self::TarLz4,
_ => Self::Unknown,
}
}
}
impl fmt::Display for Lz4Extension {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Lz4FrameKind {
#[default]
Standard,
Legacy,
Skippable,
Block,
Unknown,
}
impl Lz4FrameKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Standard => "standard",
Self::Legacy => "legacy",
Self::Skippable => "skippable",
Self::Block => "block",
Self::Unknown => "unknown",
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Lz4Profile {
Speed,
Size,
#[default]
Balanced,
Unknown,
}
impl Lz4Profile {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Speed => "speed",
Self::Size => "size",
Self::Balanced => "balanced",
Self::Unknown => "unknown",
}
}
}
#[must_use]
pub fn is_lz4_extension(extension: &str) -> bool {
!matches!(
Lz4Extension::from_extension(extension),
Lz4Extension::Unknown
)
}
#[must_use]
pub fn is_lz4_filename(name: &str) -> bool {
let parts = filename_parts(name);
match parts.as_slice() {
[.., last] if last == "lz4" => true,
[.., previous, last] if previous == "tar" && last == "lz4" => true,
_ => false,
}
}
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::{
LZ4_EXTENSIONS, Lz4Extension, Lz4FrameKind, Lz4Profile, is_lz4_extension, is_lz4_filename,
};
#[test]
fn detects_lz4_extensions() {
assert!(is_lz4_extension(".lz4"));
assert!(is_lz4_extension("tar.lz4"));
assert_eq!(
Lz4Extension::from_extension("tar.lz4"),
Lz4Extension::TarLz4
);
assert_eq!(LZ4_EXTENSIONS[0], "lz4");
}
#[test]
fn detects_lz4_filenames() {
assert!(is_lz4_filename("release.tar.lz4"));
assert!(is_lz4_filename("payload.LZ4"));
assert!(!is_lz4_filename("bundle.zip"));
}
#[test]
fn exposes_default_and_unknown_labels() {
assert_eq!(Lz4Extension::default(), Lz4Extension::Unknown);
assert_eq!(Lz4FrameKind::default().as_str(), "standard");
assert_eq!(Lz4Profile::Unknown.as_str(), "unknown");
}
}