use std::fmt;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
pub enum ArchiveFormat {
Zip,
Tar,
TarGz,
TarBz2,
TarXz,
TarZst,
TarLz4,
SevenZip,
Gz,
Bz2,
Xz,
Lz4,
Zstd,
StuffIt,
CompactPro,
Ar,
Deb,
Cab,
Rar,
Brotli,
Snappy,
Lzma,
Lzip,
Bz3,
Arc,
Unknown,
}
impl fmt::Display for ArchiveFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Zip => write!(f, "ZIP"),
Self::Tar => write!(f, "TAR"),
Self::TarGz => write!(f, "TAR.GZ"),
Self::TarBz2 => write!(f, "TAR.BZ2"),
Self::TarXz => write!(f, "TAR.XZ"),
Self::TarZst => write!(f, "TAR.ZSTD"),
Self::TarLz4 => write!(f, "TAR.LZ4"),
Self::SevenZip => write!(f, "7Z"),
Self::Gz => write!(f, "GZIP"),
Self::Bz2 => write!(f, "BZIP2"),
Self::Xz => write!(f, "XZ"),
Self::Lz4 => write!(f, "LZ4"),
Self::Zstd => write!(f, "ZSTD"),
Self::StuffIt => write!(f, "StuffIt"),
Self::CompactPro => write!(f, "Compact Pro"),
Self::Ar => write!(f, "AR"),
Self::Deb => write!(f, "DEB"),
Self::Cab => write!(f, "CAB"),
Self::Rar => write!(f, "RAR"),
Self::Brotli => write!(f, "Brotli"),
Self::Snappy => write!(f, "Snappy"),
Self::Lzma => write!(f, "LZMA"),
Self::Lzip => write!(f, "LZIP"),
Self::Bz3 => write!(f, "BZIP3"),
Self::Arc => write!(f, "ARC"),
Self::Unknown => write!(f, "Unknown"),
}
}
}
const ZIP_MAGIC: &[u8] = &[0x50, 0x4B, 0x03, 0x04];
const GZ_MAGIC: &[u8] = &[0x1F, 0x8B];
const BZ2_MAGIC: &[u8] = &[0x42, 0x5A, 0x68];
const XZ_MAGIC: &[u8] = &[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00];
const SEVENZ_MAGIC: &[u8] = &[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C];
const ZSTD_MAGIC: &[u8] = &[0x28, 0xB5, 0x2F, 0xFD];
const LZ4_FRAME_MAGIC: &[u8] = &[0x04, 0x22, 0x4D, 0x18];
const STUFFIT_MAGIC: &[u8] = b"SIT!";
const STUFFIT5_MAGIC: &[u8] = b"StuffIt";
const COMPACT_PRO_MAGIC: u8 = 0x01;
const RAR_MAGIC: &[u8] = &[0x52, 0x61, 0x72, 0x21, 0x1A, 0x07];
const CAB_MAGIC: &[u8] = &[0x4D, 0x53, 0x43, 0x46];
const AR_MAGIC: &[u8] = b"!<arch>";
const LZIP_MAGIC: &[u8] = b"LZIP";
const BZ3_MAGIC: &[u8] = b"BZ3v1";
const SNAPPY_FRAME_MAGIC: &[u8] = &[0xFF, 0x06, 0x00, 0x00, 0x73, 0x4E, 0x61, 0x50, 0x70, 0x59];
#[must_use]
pub fn detect_format(data: &[u8]) -> ArchiveFormat {
if data.len() < 4 {
return ArchiveFormat::Unknown;
}
if data.len() >= 10 && data.starts_with(SNAPPY_FRAME_MAGIC) {
return ArchiveFormat::Snappy;
}
if data.len() >= 5 && data.starts_with(BZ3_MAGIC) {
return ArchiveFormat::Bz3;
}
if data.len() >= 8 && data.starts_with(AR_MAGIC) {
if data.len() > 68 && data.get(8..24) == Some(b"debian-binary ".as_slice()) {
return ArchiveFormat::Deb;
}
return ArchiveFormat::Ar;
}
if data.len() >= 7 && data.starts_with(RAR_MAGIC) {
return ArchiveFormat::Rar;
}
if data.len() >= 6 && data.starts_with(SEVENZ_MAGIC) {
return ArchiveFormat::SevenZip;
}
if data.len() >= 6 && data.starts_with(XZ_MAGIC) {
return ArchiveFormat::Xz;
}
if data.starts_with(ZIP_MAGIC) {
return ArchiveFormat::Zip;
}
if data.starts_with(CAB_MAGIC) {
return ArchiveFormat::Cab;
}
if data.starts_with(ZSTD_MAGIC) {
return ArchiveFormat::Zstd;
}
if data.starts_with(LZ4_FRAME_MAGIC) {
return ArchiveFormat::Lz4;
}
if data.starts_with(LZIP_MAGIC) {
return ArchiveFormat::Lzip;
}
if data.starts_with(GZ_MAGIC) {
return ArchiveFormat::Gz;
}
if data.starts_with(BZ2_MAGIC) {
return ArchiveFormat::Bz2;
}
if data.starts_with(STUFFIT_MAGIC) || (data.len() >= 7 && data.starts_with(STUFFIT5_MAGIC)) {
return ArchiveFormat::StuffIt;
}
if data.first().copied() == Some(COMPACT_PRO_MAGIC) && data.get(1).copied() == Some(0x01) {
return ArchiveFormat::CompactPro;
}
if data.first().copied() == Some(0x5D) && data.get(1).copied() == Some(0x00) {
return ArchiveFormat::Lzma;
}
if data.first().copied() == Some(0x1A)
&& data.get(1).copied().is_some_and(|b| (1..=9).contains(&b))
{
return ArchiveFormat::Arc;
}
if data.get(257..262) == Some(b"ustar".as_slice()) {
return ArchiveFormat::Tar;
}
ArchiveFormat::Unknown
}
#[must_use]
#[allow(clippy::case_sensitive_file_extension_comparisons)]
pub fn detect_format_from_path(path: &Path, data: &[u8]) -> ArchiveFormat {
let magic = detect_format(data);
if !matches!(
magic,
ArchiveFormat::Gz
| ArchiveFormat::Bz2
| ArchiveFormat::Xz
| ArchiveFormat::Zstd
| ArchiveFormat::Lz4
| ArchiveFormat::Unknown
) {
return magic;
}
let name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("")
.to_ascii_lowercase();
if name.ends_with(".tar.gz") || name.ends_with(".tgz") {
return ArchiveFormat::TarGz;
}
if name.ends_with(".tar.bz2") || name.ends_with(".tbz2") {
return ArchiveFormat::TarBz2;
}
if name.ends_with(".tar.xz") || name.ends_with(".txz") {
return ArchiveFormat::TarXz;
}
if name.ends_with(".tar.zst") || name.ends_with(".tzst") {
return ArchiveFormat::TarZst;
}
if name.ends_with(".tar.lz4") {
return ArchiveFormat::TarLz4;
}
if name.ends_with(".tar") {
return ArchiveFormat::Tar;
}
if name.ends_with(".sit") || name.ends_with(".sitx") || name.ends_with(".sea") {
return ArchiveFormat::StuffIt;
}
if name.ends_with(".cpt") {
return ArchiveFormat::CompactPro;
}
if name.ends_with(".7z") {
return ArchiveFormat::SevenZip;
}
if name.ends_with(".deb") || name.ends_with(".udeb") {
return ArchiveFormat::Deb;
}
if name.ends_with(".cab") {
return ArchiveFormat::Cab;
}
if name.ends_with(".rar") || name.ends_with(".cbr") {
return ArchiveFormat::Rar;
}
if name.ends_with(".br") {
return ArchiveFormat::Brotli;
}
if name.ends_with(".sz") || name.ends_with(".snappy") {
return ArchiveFormat::Snappy;
}
if name.ends_with(".lzma") {
return ArchiveFormat::Lzma;
}
if name.ends_with(".bz3") {
return ArchiveFormat::Bz3;
}
if name.ends_with(".lz") {
return ArchiveFormat::Lzip;
}
if name.ends_with(".arc") {
return ArchiveFormat::Arc;
}
if name.ends_with(".a") {
return ArchiveFormat::Ar;
}
magic
}
#[derive(Debug, serde::Serialize)]
pub struct FormatInfo {
pub format: ArchiveFormat,
pub description: String,
pub mime_type: String,
pub is_archive: bool,
pub is_compressed: bool,
}
#[must_use]
#[allow(clippy::too_many_lines)]
pub fn format_info(format: ArchiveFormat) -> FormatInfo {
match format {
ArchiveFormat::Zip => FormatInfo {
format,
description: "ZIP archive (PKZIP compatible)".into(),
mime_type: "application/zip".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Tar => FormatInfo {
format,
description: "POSIX TAR archive (uncompressed)".into(),
mime_type: "application/x-tar".into(),
is_archive: true,
is_compressed: false,
},
ArchiveFormat::TarGz => FormatInfo {
format,
description: "TAR archive compressed with gzip".into(),
mime_type: "application/gzip".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::TarBz2 => FormatInfo {
format,
description: "TAR archive compressed with bzip2".into(),
mime_type: "application/x-bzip2".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::TarXz => FormatInfo {
format,
description: "TAR archive compressed with XZ/LZMA2".into(),
mime_type: "application/x-xz".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::TarZst => FormatInfo {
format,
description: "TAR archive compressed with Zstandard".into(),
mime_type: "application/zstd".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::TarLz4 => FormatInfo {
format,
description: "TAR archive compressed with LZ4".into(),
mime_type: "application/x-lz4".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::SevenZip => FormatInfo {
format,
description: "7-Zip archive (LZMA/LZMA2)".into(),
mime_type: "application/x-7z-compressed".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Gz => FormatInfo {
format,
description: "Gzip compressed file (RFC 1952)".into(),
mime_type: "application/gzip".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Bz2 => FormatInfo {
format,
description: "Bzip2 compressed file".into(),
mime_type: "application/x-bzip2".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Xz => FormatInfo {
format,
description: "XZ compressed file (LZMA2)".into(),
mime_type: "application/x-xz".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Lz4 => FormatInfo {
format,
description: "LZ4 compressed file (frame format)".into(),
mime_type: "application/x-lz4".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Zstd => FormatInfo {
format,
description: "Zstandard compressed file".into(),
mime_type: "application/zstd".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::StuffIt => FormatInfo {
format,
description: "StuffIt archive (classic Mac OS)".into(),
mime_type: "application/x-stuffit".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::CompactPro => FormatInfo {
format,
description: "Compact Pro archive (classic Mac OS)".into(),
mime_type: "application/x-compact-pro".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Ar => FormatInfo {
format,
description: "Unix AR archive".into(),
mime_type: "application/x-archive".into(),
is_archive: true,
is_compressed: false,
},
ArchiveFormat::Deb => FormatInfo {
format,
description: "Debian package (ar + tar)".into(),
mime_type: "application/vnd.debian.binary-package".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Cab => FormatInfo {
format,
description: "Windows Cabinet archive".into(),
mime_type: "application/vnd.ms-cab-compressed".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Rar => FormatInfo {
format,
description: "RAR archive (v4/v5)".into(),
mime_type: "application/vnd.rar".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Brotli => FormatInfo {
format,
description: "Brotli compressed file".into(),
mime_type: "application/x-brotli".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Snappy => FormatInfo {
format,
description: "Snappy compressed file (frame format)".into(),
mime_type: "application/x-snappy-framed".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Lzma => FormatInfo {
format,
description: "LZMA compressed file (standalone)".into(),
mime_type: "application/x-lzma".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Bz3 => FormatInfo {
format,
description: "BZip3 compressed file".into(),
mime_type: "application/x-bzip3".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Lzip => FormatInfo {
format,
description: "Lzip compressed file (LZMA)".into(),
mime_type: "application/x-lzip".into(),
is_archive: false,
is_compressed: true,
},
ArchiveFormat::Arc => FormatInfo {
format,
description: "ARC archive (SEA/PKWare)".into(),
mime_type: "application/x-arc".into(),
is_archive: true,
is_compressed: true,
},
ArchiveFormat::Unknown => FormatInfo {
format,
description: "Unknown format".into(),
mime_type: "application/octet-stream".into(),
is_archive: false,
is_compressed: false,
},
}
}
#[cfg(test)]
#[allow(clippy::missing_panics_doc)]
mod tests {
use super::*;
#[test]
fn test_detect_zip() {
let data = [0x50, 0x4B, 0x03, 0x04, 0x00, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Zip);
}
#[test]
fn test_detect_gzip() {
let data = [0x1F, 0x8B, 0x08, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Gz);
}
#[test]
fn test_detect_xz() {
let data = [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Xz);
}
#[test]
fn test_detect_lz4_frame() {
let data = [0x04, 0x22, 0x4D, 0x18, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Lz4);
}
#[test]
fn test_detect_zstd() {
let data = [0x28, 0xB5, 0x2F, 0xFD, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Zstd);
}
#[test]
fn test_detect_bz2() {
let data = [0x42, 0x5A, 0x68, 0x39, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Bz2);
}
#[test]
fn test_detect_7z() {
let data = [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::SevenZip);
}
#[test]
fn test_detect_stuffit() {
let data = b"SIT!\x00\x00\x00\x00";
assert_eq!(detect_format(data), ArchiveFormat::StuffIt);
}
#[test]
fn test_detect_compact_pro() {
let data = [0x01, 0x01, 0x00, 0x00, 0x00, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::CompactPro);
}
#[test]
fn test_detect_unknown() {
let data = [0xFF, 0xFE, 0x00, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Unknown);
}
#[test]
fn test_detect_too_short() {
let data = [0x50, 0x4B];
assert_eq!(detect_format(&data), ArchiveFormat::Unknown);
}
#[test]
fn test_detect_empty_data() {
assert_eq!(detect_format(&[]), ArchiveFormat::Unknown);
}
#[test]
fn test_detect_single_byte() {
assert_eq!(detect_format(&[0xFF]), ArchiveFormat::Unknown);
}
#[test]
fn test_detect_three_bytes() {
assert_eq!(detect_format(&[0x50, 0x4B, 0x03]), ArchiveFormat::Unknown);
}
#[test]
fn test_detect_stuffit5() {
let data = b"StuffIt\x05\x00\x00\x00\x00\x00\x00\x00";
assert_eq!(detect_format(data), ArchiveFormat::StuffIt);
}
#[test]
fn test_detect_rar_v5() {
let data = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Rar);
}
#[test]
fn test_detect_rar_v4() {
let data = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00, 0xCF];
assert_eq!(detect_format(&data), ArchiveFormat::Rar);
}
#[test]
fn test_detect_cab() {
let data = [0x4D, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Cab);
}
#[test]
fn test_detect_bz3() {
let data = b"BZ3v1\x00\x00\x00";
assert_eq!(detect_format(data), ArchiveFormat::Bz3);
}
#[test]
fn test_extension_bz3() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("file.bz3");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Bz3);
}
#[test]
fn test_detect_ar() {
let data = b"!<arch>\n";
assert_eq!(detect_format(data), ArchiveFormat::Ar);
}
#[test]
fn test_detect_lzma() {
let data = [0x5D, 0x00, 0x00, 0x80, 0x00];
assert_eq!(detect_format(&data), ArchiveFormat::Lzma);
}
#[test]
fn test_detect_lzip() {
let data = b"LZIP\x01\x33\x00\x26";
assert_eq!(detect_format(data), ArchiveFormat::Lzip);
}
#[test]
fn test_detect_arc() {
let data = [0x1A, 0x08, 0x43, 0x4F, 0x4D, 0x50];
assert_eq!(detect_format(&data), ArchiveFormat::Arc);
}
#[test]
fn test_detect_snappy_frame() {
let data = [
0xFF, 0x06, 0x00, 0x00, 0x73, 0x4E, 0x61, 0x50, 0x70, 0x59, 0x00,
];
assert_eq!(detect_format(&data), ArchiveFormat::Snappy);
}
#[test]
fn test_extension_tar_gz() {
let data = [0x1F, 0x8B, 0x08, 0x00, 0x00];
let path = Path::new("test.tar.gz");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarGz);
}
#[test]
fn test_extension_tgz() {
let data = [0x1F, 0x8B, 0x08, 0x00, 0x00];
let path = Path::new("test.tgz");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarGz);
}
#[test]
fn test_extension_tar_bz2() {
let data = [0x42, 0x5A, 0x68, 0x39, 0x00];
let path = Path::new("test.tar.bz2");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarBz2);
}
#[test]
fn test_extension_tar_xz() {
let data = [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00];
let path = Path::new("test.tar.xz");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarXz);
}
#[test]
fn test_extension_tar_zst() {
let data = [0x28, 0xB5, 0x2F, 0xFD, 0x00];
let path = Path::new("test.tar.zst");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarZst);
}
#[test]
fn test_extension_tar_lz4() {
let data = [0x04, 0x22, 0x4D, 0x18, 0x00];
let path = Path::new("test.tar.lz4");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::TarLz4);
}
#[test]
fn test_extension_sit() {
let data = b"SIT!\x00\x00\x00\x00";
let path = Path::new("test.sit");
assert_eq!(detect_format_from_path(path, data), ArchiveFormat::StuffIt);
}
#[test]
fn test_extension_7z() {
let data = [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, 0x00];
let path = Path::new("test.7z");
assert_eq!(
detect_format_from_path(path, &data),
ArchiveFormat::SevenZip
);
}
#[test]
fn test_extension_rar() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("test.rar");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Rar);
}
#[test]
fn test_extension_cbr() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("test.cbr");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Rar);
}
#[test]
fn test_extension_deb() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("package.deb");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Deb);
}
#[test]
fn test_extension_cab() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("setup.cab");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Cab);
}
#[test]
fn test_extension_brotli() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("file.br");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Brotli);
}
#[test]
fn test_extension_lzma() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("file.lzma");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Lzma);
}
#[test]
fn test_extension_lz() {
let data = [0x00, 0x00, 0x00, 0x00];
let path = Path::new("file.lz");
assert_eq!(detect_format_from_path(path, &data), ArchiveFormat::Lzip);
}
#[test]
fn test_format_info_all_variants() {
let formats = [
ArchiveFormat::Zip,
ArchiveFormat::Tar,
ArchiveFormat::TarGz,
ArchiveFormat::TarBz2,
ArchiveFormat::TarXz,
ArchiveFormat::TarZst,
ArchiveFormat::TarLz4,
ArchiveFormat::SevenZip,
ArchiveFormat::Gz,
ArchiveFormat::Bz2,
ArchiveFormat::Xz,
ArchiveFormat::Lz4,
ArchiveFormat::Zstd,
ArchiveFormat::StuffIt,
ArchiveFormat::CompactPro,
ArchiveFormat::Ar,
ArchiveFormat::Deb,
ArchiveFormat::Cab,
ArchiveFormat::Rar,
ArchiveFormat::Brotli,
ArchiveFormat::Snappy,
ArchiveFormat::Lzma,
ArchiveFormat::Lzip,
ArchiveFormat::Bz3,
ArchiveFormat::Arc,
ArchiveFormat::Unknown,
];
for fmt in &formats {
let info = format_info(*fmt);
assert!(!info.description.is_empty());
assert!(!info.mime_type.is_empty());
}
}
#[test]
fn test_display_all_variants() {
let formats = [
ArchiveFormat::Zip,
ArchiveFormat::Tar,
ArchiveFormat::TarGz,
ArchiveFormat::TarBz2,
ArchiveFormat::TarXz,
ArchiveFormat::TarZst,
ArchiveFormat::TarLz4,
ArchiveFormat::SevenZip,
ArchiveFormat::Gz,
ArchiveFormat::Bz2,
ArchiveFormat::Xz,
ArchiveFormat::Lz4,
ArchiveFormat::Zstd,
ArchiveFormat::StuffIt,
ArchiveFormat::CompactPro,
ArchiveFormat::Ar,
ArchiveFormat::Deb,
ArchiveFormat::Cab,
ArchiveFormat::Rar,
ArchiveFormat::Brotli,
ArchiveFormat::Snappy,
ArchiveFormat::Lzma,
ArchiveFormat::Lzip,
ArchiveFormat::Bz3,
ArchiveFormat::Arc,
ArchiveFormat::Unknown,
];
for fmt in &formats {
let s = fmt.to_string();
assert!(!s.is_empty());
}
}
}