use std::fmt::Display; use std::io::prelude::*; use std::io::Write; use anyhow::Result; use crate::impl_selectable_content; use crate::log_line; use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder}; use flate2::Compression; use lzma::LzmaWriter; use zip::write::FileOptions; /// Different kind of compression methods #[derive(Debug)] pub enum CompressionMethod { ZIP, DEFLATE, GZ, ZLIB, LZMA, } impl Display for CompressionMethod { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Self::ZIP => write!(f, "ZIP: archive.zip"), Self::DEFLATE => write!(f, "DEFLATE: archive.tar.gz"), Self::LZMA => write!(f, "LZMA: archive.tar.xz"), Self::GZ => write!(f, "GZ: archive.tar.gz"), Self::ZLIB => write!(f, "ZLIB: archive.tar.xz"), } } } /// Holds a vector of CompressionMethod and a few methods to compress some files. #[derive(Debug)] pub struct Compresser { content: Vec, pub index: usize, } impl Default for Compresser { fn default() -> Self { Self { content: vec![ CompressionMethod::ZIP, CompressionMethod::LZMA, CompressionMethod::ZLIB, CompressionMethod::GZ, CompressionMethod::DEFLATE, ], index: 0, } } } impl Compresser { /// Archive the files with tar and compress them with the selected method. /// The compression method is chosen by the user. /// Archive is created `here` which should be the path of the selected tab. pub fn compress(&self, files: Vec, here: &std::path::Path) -> Result<()> { let Some(selected) = self.selected() else { return Ok(()); }; match selected { CompressionMethod::DEFLATE => { Self::defl(Self::archive(here, "archive.tar.gz")?, files)? } CompressionMethod::GZ => Self::gzip(Self::archive(here, "archive.tar.gz")?, files)?, CompressionMethod::ZLIB => Self::zlib(Self::archive(here, "archive.tar.xz")?, files)?, CompressionMethod::ZIP => Self::zip(Self::archive(here, "archive.zip")?, files)?, CompressionMethod::LZMA => Self::lzma(Self::archive(here, "archive.tar.xz")?, files)?, } log_line!("Compressed with {selected}"); Ok(()) } fn make_tar(files: Vec, mut archive: tar::Builder) -> Result<()> where W: Write, { for path in files.iter() { if path.starts_with("..") { continue; } if path.is_dir() { archive.append_dir_all(path, path)?; } else { archive.append_path(path)?; } } Ok(()) } fn archive(here: &std::path::Path, archive_name: &str) -> Result { let mut full_path = here.to_path_buf(); full_path.push(archive_name); Ok(std::fs::File::create(full_path)?) } fn gzip(archive: std::fs::File, files: Vec) -> Result<()> { let mut encoder = GzEncoder::new(archive, Compression::default()); // Create tar archive and compress files Self::make_tar(files, tar::Builder::new(&mut encoder))?; // Finish Gzip file encoder.finish()?; Ok(()) } fn defl(archive: std::fs::File, files: Vec) -> Result<()> { let mut encoder = DeflateEncoder::new(archive, Compression::default()); // Create tar archive and compress files Self::make_tar(files, tar::Builder::new(&mut encoder))?; // Finish deflate file encoder.finish()?; Ok(()) } fn zlib(archive: std::fs::File, files: Vec) -> Result<()> { let mut encoder = ZlibEncoder::new(archive, Compression::default()); // Create tar archive and compress files Self::make_tar(files, tar::Builder::new(&mut encoder))?; // Finish zlib file encoder.finish()?; Ok(()) } fn lzma(archive: std::fs::File, files: Vec) -> Result<()> { let mut encoder = LzmaWriter::new_compressor(archive, 6)?; // Create tar archive and compress files Self::make_tar(files, tar::Builder::new(&mut encoder))?; // Finish lzma file encoder.finish()?; Ok(()) } fn zip(archive: std::fs::File, files: Vec) -> Result<()> { let mut zip = zip::ZipWriter::new(archive); for file in files.iter() { zip.start_file( file.to_str().unwrap(), FileOptions::default().compression_method(zip::CompressionMethod::Bzip2), )?; let mut buffer = Vec::new(); let mut content = std::fs::File::open(file)?; content.read_to_end(&mut buffer)?; zip.write_all(&buffer)?; } // Finish zip file zip.finish()?; Ok(()) } } impl_selectable_content!(CompressionMethod, Compresser);