tauri_api/file/
extract.rsuse either::{self, Either};
use std::fs;
use std::io;
use std::path;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ArchiveFormat {
Tar(Option<Compression>),
Plain(Option<Compression>),
Zip,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Compression {
Gz,
}
#[derive(Debug)]
pub struct Extract<'a> {
source: &'a path::Path,
archive_format: Option<ArchiveFormat>,
}
fn detect_archive_type(path: &path::Path) -> ArchiveFormat {
match path.extension() {
Some(extension) if extension == std::ffi::OsStr::new("zip") => ArchiveFormat::Zip,
Some(extension) if extension == std::ffi::OsStr::new("tar") => ArchiveFormat::Tar(None),
Some(extension) if extension == std::ffi::OsStr::new("gz") => match path
.file_stem()
.map(|e| path::Path::new(e))
.and_then(|f| f.extension())
{
Some(extension) if extension == std::ffi::OsStr::new("tar") => {
ArchiveFormat::Tar(Some(Compression::Gz))
}
_ => ArchiveFormat::Plain(Some(Compression::Gz)),
},
_ => ArchiveFormat::Plain(None),
}
}
impl<'a> Extract<'a> {
pub fn from_source(source: &'a path::Path) -> Extract<'a> {
Self {
source,
archive_format: None,
}
}
pub fn archive_format(&mut self, format: ArchiveFormat) -> &mut Self {
self.archive_format = Some(format);
self
}
fn get_archive_reader(
source: fs::File,
compression: Option<Compression>,
) -> Either<fs::File, flate2::read::GzDecoder<fs::File>> {
match compression {
Some(Compression::Gz) => Either::Right(flate2::read::GzDecoder::new(source)),
None => Either::Left(source),
}
}
pub fn extract_into(&self, into_dir: &path::Path) -> crate::Result<()> {
let source = fs::File::open(self.source)?;
let archive = self
.archive_format
.unwrap_or_else(|| detect_archive_type(&self.source));
match archive {
ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => {
let mut reader = Self::get_archive_reader(source, compression);
match archive {
ArchiveFormat::Plain(_) => {
match fs::create_dir_all(into_dir) {
Ok(_) => (),
Err(e) => {
if e.kind() != io::ErrorKind::AlreadyExists {
return Err(e.into());
}
}
}
let file_name = self
.source
.file_name()
.ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
let mut out_path = into_dir.join(file_name);
out_path.set_extension("");
let mut out_file = fs::File::create(&out_path)?;
io::copy(&mut reader, &mut out_file)?;
}
ArchiveFormat::Tar(_) => {
let mut archive = tar::Archive::new(reader);
archive.unpack(into_dir)?;
}
_ => unreachable!(),
};
}
ArchiveFormat::Zip => {
let mut archive = zip::ZipArchive::new(source)?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let path = into_dir.join(file.name());
let mut output = fs::File::create(path)?;
io::copy(&mut file, &mut output)?;
}
}
};
Ok(())
}
pub fn extract_file<T: AsRef<path::Path>>(
&self,
into_dir: &path::Path,
file_to_extract: T,
) -> crate::Result<()> {
let file_to_extract = file_to_extract.as_ref();
let source = fs::File::open(self.source)?;
let archive = self
.archive_format
.unwrap_or_else(|| detect_archive_type(&self.source));
match archive {
ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => {
let mut reader = Self::get_archive_reader(source, compression);
match archive {
ArchiveFormat::Plain(_) => {
match fs::create_dir_all(into_dir) {
Ok(_) => (),
Err(e) => {
if e.kind() != io::ErrorKind::AlreadyExists {
return Err(e.into());
}
}
}
let file_name = file_to_extract
.file_name()
.ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
let out_path = into_dir.join(file_name);
let mut out_file = fs::File::create(&out_path)?;
io::copy(&mut reader, &mut out_file)?;
}
ArchiveFormat::Tar(_) => {
let mut archive = tar::Archive::new(reader);
let mut entry = archive
.entries()?
.filter_map(|e| e.ok())
.find(|e| e.path().ok().filter(|p| p == file_to_extract).is_some())
.ok_or_else(|| {
crate::Error::Extract(format!(
"Could not find the required path in the archive: {:?}",
file_to_extract
))
})?;
entry.unpack_in(into_dir)?;
}
_ => {
panic!("Unreasonable code");
}
};
}
ArchiveFormat::Zip => {
let mut archive = zip::ZipArchive::new(source)?;
let mut file = archive.by_name(
file_to_extract
.to_str()
.expect("Could not convert file to str"),
)?;
let mut output = fs::File::create(into_dir.join(file.name()))?;
io::copy(&mut file, &mut output)?;
}
};
Ok(())
}
}