tauri_api/file/
extract.rs1use either::{self, Either};
2
3use std::fs;
4use std::io;
5use std::path;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum ArchiveFormat {
10 Tar(Option<Compression>),
12 Plain(Option<Compression>),
14 Zip,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum Compression {
21 Gz,
23}
24
25#[derive(Debug)]
27pub struct Extract<'a> {
28 source: &'a path::Path,
29 archive_format: Option<ArchiveFormat>,
30}
31
32fn detect_archive_type(path: &path::Path) -> ArchiveFormat {
33 match path.extension() {
34 Some(extension) if extension == std::ffi::OsStr::new("zip") => ArchiveFormat::Zip,
35 Some(extension) if extension == std::ffi::OsStr::new("tar") => ArchiveFormat::Tar(None),
36 Some(extension) if extension == std::ffi::OsStr::new("gz") => match path
37 .file_stem()
38 .map(|e| path::Path::new(e))
39 .and_then(|f| f.extension())
40 {
41 Some(extension) if extension == std::ffi::OsStr::new("tar") => {
42 ArchiveFormat::Tar(Some(Compression::Gz))
43 }
44 _ => ArchiveFormat::Plain(Some(Compression::Gz)),
45 },
46 _ => ArchiveFormat::Plain(None),
47 }
48}
49
50impl<'a> Extract<'a> {
51 pub fn from_source(source: &'a path::Path) -> Extract<'a> {
53 Self {
54 source,
55 archive_format: None,
56 }
57 }
58
59 pub fn archive_format(&mut self, format: ArchiveFormat) -> &mut Self {
62 self.archive_format = Some(format);
63 self
64 }
65
66 fn get_archive_reader(
67 source: fs::File,
68 compression: Option<Compression>,
69 ) -> Either<fs::File, flate2::read::GzDecoder<fs::File>> {
70 match compression {
71 Some(Compression::Gz) => Either::Right(flate2::read::GzDecoder::new(source)),
72 None => Either::Left(source),
73 }
74 }
75
76 pub fn extract_into(&self, into_dir: &path::Path) -> crate::Result<()> {
80 let source = fs::File::open(self.source)?;
81 let archive = self
82 .archive_format
83 .unwrap_or_else(|| detect_archive_type(&self.source));
84
85 match archive {
86 ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => {
87 let mut reader = Self::get_archive_reader(source, compression);
88
89 match archive {
90 ArchiveFormat::Plain(_) => {
91 match fs::create_dir_all(into_dir) {
92 Ok(_) => (),
93 Err(e) => {
94 if e.kind() != io::ErrorKind::AlreadyExists {
95 return Err(e.into());
96 }
97 }
98 }
99 let file_name = self
100 .source
101 .file_name()
102 .ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
103 let mut out_path = into_dir.join(file_name);
104 out_path.set_extension("");
105 let mut out_file = fs::File::create(&out_path)?;
106 io::copy(&mut reader, &mut out_file)?;
107 }
108 ArchiveFormat::Tar(_) => {
109 let mut archive = tar::Archive::new(reader);
110 archive.unpack(into_dir)?;
111 }
112 _ => unreachable!(),
113 };
114 }
115 ArchiveFormat::Zip => {
116 let mut archive = zip::ZipArchive::new(source)?;
117 for i in 0..archive.len() {
118 let mut file = archive.by_index(i)?;
119 let path = into_dir.join(file.name());
120 let mut output = fs::File::create(path)?;
121 io::copy(&mut file, &mut output)?;
122 }
123 }
124 };
125 Ok(())
126 }
127
128 pub fn extract_file<T: AsRef<path::Path>>(
132 &self,
133 into_dir: &path::Path,
134 file_to_extract: T,
135 ) -> crate::Result<()> {
136 let file_to_extract = file_to_extract.as_ref();
137 let source = fs::File::open(self.source)?;
138 let archive = self
139 .archive_format
140 .unwrap_or_else(|| detect_archive_type(&self.source));
141
142 match archive {
143 ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => {
144 let mut reader = Self::get_archive_reader(source, compression);
145
146 match archive {
147 ArchiveFormat::Plain(_) => {
148 match fs::create_dir_all(into_dir) {
149 Ok(_) => (),
150 Err(e) => {
151 if e.kind() != io::ErrorKind::AlreadyExists {
152 return Err(e.into());
153 }
154 }
155 }
156 let file_name = file_to_extract
157 .file_name()
158 .ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
159 let out_path = into_dir.join(file_name);
160 let mut out_file = fs::File::create(&out_path)?;
161 io::copy(&mut reader, &mut out_file)?;
162 }
163 ArchiveFormat::Tar(_) => {
164 let mut archive = tar::Archive::new(reader);
165 let mut entry = archive
166 .entries()?
167 .filter_map(|e| e.ok())
168 .find(|e| e.path().ok().filter(|p| p == file_to_extract).is_some())
169 .ok_or_else(|| {
170 crate::Error::Extract(format!(
171 "Could not find the required path in the archive: {:?}",
172 file_to_extract
173 ))
174 })?;
175 entry.unpack_in(into_dir)?;
176 }
177 _ => {
178 panic!("Unreasonable code");
179 }
180 };
181 }
182 ArchiveFormat::Zip => {
183 let mut archive = zip::ZipArchive::new(source)?;
184 let mut file = archive.by_name(
185 file_to_extract
186 .to_str()
187 .expect("Could not convert file to str"),
188 )?;
189 let mut output = fs::File::create(into_dir.join(file.name()))?;
190 io::copy(&mut file, &mut output)?;
191 }
192 };
193 Ok(())
194 }
195}