subx_cli/core/archive/
mod.rs1mod common;
36mod rar;
37mod sevenz;
38mod targz;
39mod zip;
40
41use std::io;
42use std::path::{Path, PathBuf};
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum ArchiveFormat {
47 Zip,
49 Rar,
51 SevenZip,
53 TarGz,
55}
56
57pub fn detect_format(path: &Path) -> Option<ArchiveFormat> {
64 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
66 let lower = name.to_ascii_lowercase();
67 if lower.ends_with(".tar.gz") {
68 return Some(ArchiveFormat::TarGz);
69 }
70 }
71
72 let ext = path.extension()?.to_str()?.to_ascii_lowercase();
74 match ext.as_str() {
75 "zip" => Some(ArchiveFormat::Zip),
76 "rar" => Some(ArchiveFormat::Rar),
77 "7z" => Some(ArchiveFormat::SevenZip),
78 "tgz" => Some(ArchiveFormat::TarGz),
79 _ => None,
80 }
81}
82
83pub fn extract_archive(archive_path: &Path, dest_dir: &Path) -> io::Result<Vec<PathBuf>> {
93 let format = detect_format(archive_path).ok_or_else(|| {
94 io::Error::new(
95 io::ErrorKind::InvalidInput,
96 format!("Unrecognised archive format: {}", archive_path.display()),
97 )
98 })?;
99
100 match format {
101 ArchiveFormat::Zip => zip::extract_zip(archive_path, dest_dir),
102 ArchiveFormat::Rar => rar::extract_rar(archive_path, dest_dir),
103 ArchiveFormat::SevenZip => sevenz::extract_7z(archive_path, dest_dir),
104 ArchiveFormat::TarGz => targz::extract_tar_gz(archive_path, dest_dir),
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_detect_format_zip() {
114 assert_eq!(
115 detect_format(Path::new("test.zip")),
116 Some(ArchiveFormat::Zip)
117 );
118 }
119
120 #[test]
121 fn test_detect_format_zip_uppercase() {
122 assert_eq!(
123 detect_format(Path::new("test.ZIP")),
124 Some(ArchiveFormat::Zip)
125 );
126 }
127
128 #[test]
129 fn test_detect_format_rar() {
130 assert_eq!(
131 detect_format(Path::new("test.rar")),
132 Some(ArchiveFormat::Rar)
133 );
134 }
135
136 #[test]
137 fn test_detect_format_rar_mixed_case() {
138 assert_eq!(
139 detect_format(Path::new("test.Rar")),
140 Some(ArchiveFormat::Rar)
141 );
142 }
143
144 #[test]
145 fn test_detect_format_7z() {
146 assert_eq!(
147 detect_format(Path::new("test.7z")),
148 Some(ArchiveFormat::SevenZip)
149 );
150 }
151
152 #[test]
153 fn test_detect_format_7z_uppercase() {
154 assert_eq!(
155 detect_format(Path::new("test.7Z")),
156 Some(ArchiveFormat::SevenZip)
157 );
158 }
159
160 #[test]
161 fn test_detect_format_tar_gz() {
162 assert_eq!(
163 detect_format(Path::new("test.tar.gz")),
164 Some(ArchiveFormat::TarGz)
165 );
166 }
167
168 #[test]
169 fn test_detect_format_tar_gz_uppercase() {
170 assert_eq!(
171 detect_format(Path::new("test.TAR.GZ")),
172 Some(ArchiveFormat::TarGz)
173 );
174 }
175
176 #[test]
177 fn test_detect_format_tgz() {
178 assert_eq!(
179 detect_format(Path::new("test.tgz")),
180 Some(ArchiveFormat::TarGz)
181 );
182 }
183
184 #[test]
185 fn test_detect_format_tgz_uppercase() {
186 assert_eq!(
187 detect_format(Path::new("test.TGZ")),
188 Some(ArchiveFormat::TarGz)
189 );
190 }
191
192 #[test]
193 fn test_detect_format_tar_bz2_none() {
194 assert_eq!(detect_format(Path::new("test.tar.bz2")), None);
195 }
196
197 #[test]
198 fn test_detect_format_plain_gz_none() {
199 assert_eq!(detect_format(Path::new("test.gz")), None);
200 }
201
202 #[test]
203 fn test_detect_format_srt_none() {
204 assert_eq!(detect_format(Path::new("test.srt")), None);
205 }
206
207 #[test]
208 fn test_detect_format_no_extension_none() {
209 assert_eq!(detect_format(Path::new("testfile")), None);
210 }
211
212 #[test]
213 fn test_extract_archive_unknown_format() {
214 let tmp = tempfile::tempdir().unwrap();
215 let path = tmp.path().join("test.tar.bz2");
216 std::fs::File::create(&path).unwrap();
217
218 let result = extract_archive(&path, tmp.path());
219 assert!(result.is_err());
220 assert!(result.unwrap_err().to_string().contains("Unrecognised"));
221 }
222
223 #[cfg(not(feature = "archive-rar"))]
224 #[test]
225 fn test_extract_rar_disabled_feature() {
226 let tmp = tempfile::tempdir().unwrap();
227 let path = tmp.path().join("test.rar");
228 std::fs::File::create(&path).unwrap();
229
230 let result = extract_archive(&path, tmp.path());
231 assert!(result.is_err());
232 assert!(result.unwrap_err().to_string().contains("not compiled in"));
233 }
234}