1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
7
8pub const CAB_EXTENSION: &str = "cab";
10pub const CAB_EXTENSIONS: &[&str] = &["cab"];
12
13#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub enum CabFormat {
16 Cabinet,
18 #[default]
20 Unknown,
21}
22
23impl CabFormat {
24 #[must_use]
26 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::Cabinet => "cabinet",
29 Self::Unknown => "unknown",
30 }
31 }
32}
33
34impl fmt::Display for CabFormat {
35 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36 formatter.write_str(self.as_str())
37 }
38}
39
40#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
42pub enum CabCompressionMethod {
43 None,
45 MsZip,
47 Quantum,
49 Lzx,
51 #[default]
53 Unknown,
54}
55
56impl CabCompressionMethod {
57 #[must_use]
59 pub const fn as_str(self) -> &'static str {
60 match self {
61 Self::None => "none",
62 Self::MsZip => "mszip",
63 Self::Quantum => "quantum",
64 Self::Lzx => "lzx",
65 Self::Unknown => "unknown",
66 }
67 }
68}
69
70impl fmt::Display for CabCompressionMethod {
71 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
72 formatter.write_str(self.as_str())
73 }
74}
75
76#[must_use]
78pub fn is_cab_extension(extension: &str) -> bool {
79 normalize_extension(extension) == "cab"
80}
81
82#[must_use]
84pub fn is_cab_filename(name: &str) -> bool {
85 let parts = filename_parts(name);
86
87 matches!(parts.as_slice(), [.., last] if last == "cab")
88}
89
90fn normalize_extension(extension: &str) -> String {
91 extension
92 .trim()
93 .trim_start_matches('.')
94 .to_ascii_lowercase()
95}
96
97fn filename_parts(name: &str) -> Vec<String> {
98 name.trim()
99 .to_ascii_lowercase()
100 .rsplit(['/', '\\'])
101 .next()
102 .unwrap_or_default()
103 .trim_start_matches('.')
104 .split('.')
105 .filter(|part| !part.is_empty())
106 .map(str::to_owned)
107 .collect()
108}
109
110#[cfg(test)]
111mod tests {
112 use super::{
113 CAB_EXTENSIONS, CabCompressionMethod, CabFormat, is_cab_extension, is_cab_filename,
114 };
115
116 #[test]
117 fn detects_cab_extensions() {
118 assert!(is_cab_extension(".cab"));
119 assert_eq!(CAB_EXTENSIONS[0], "cab");
120 }
121
122 #[test]
123 fn detects_cab_filenames() {
124 assert!(is_cab_filename("driver.cab"));
125 assert!(is_cab_filename("DRIVER.CAB"));
126 assert!(!is_cab_filename("bundle.zip"));
127 }
128
129 #[test]
130 fn exposes_default_and_unknown_labels() {
131 assert_eq!(CabFormat::default(), CabFormat::Unknown);
132 assert_eq!(CabFormat::Cabinet.as_str(), "cabinet");
133 assert_eq!(
134 CabCompressionMethod::default(),
135 CabCompressionMethod::Unknown
136 );
137 assert_eq!(CabCompressionMethod::MsZip.as_str(), "mszip");
138 }
139}