diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index 0cdf48d..ba3ec85 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -1,10 +1,7 @@ -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::{fs, io, path::{Path, PathBuf}}; use colored::Colorize; -use zip; +use zip::{self, read::ZipFile}; use crate::{error::{self, OuchResult}, utils}; use crate::file::File; @@ -14,9 +11,59 @@ use super::decompressor::Decompressor; pub struct ZipDecompressor {} impl ZipDecompressor { + + fn check_for_comments(file: &ZipFile) { + let comment = file.comment(); + if !comment.is_empty() { + println!("{}: Comment in {}: {}", "info".yellow(), file.name(), comment); + } + } + fn unpack_files(from: &Path, into: &Path) -> OuchResult> { + + let mut unpacked_files = vec![]; + // placeholder return - Err(error::Error::IOError) + println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), from); + + let file = fs::File::open(from)?; + let mut archive = zip::ZipArchive::new(file)?; + + for idx in 0..archive.len() { + let mut file = archive.by_index(idx)?; + let file_path = match file.enclosed_name() { + Some(path) => path.to_owned(), + None => continue, + }; + + let file_path = into.join(file_path); + + Self::check_for_comments(&file); + + if (&*file.name()).ends_with('/') { + println!("File {} extracted to \"{}\"", idx, file_path.display()); + fs::create_dir_all(&file_path)?; + } else { + println!( + "{}: \"{}\" extracted. ({} bytes)", + "info".yellow(), + file_path.display(), + file.size() + ); + if let Some(p) = file_path.parent() { + if !p.exists() { + fs::create_dir_all(&p).unwrap(); + } + } + let mut outfile = fs::File::create(&file_path).unwrap(); + io::copy(&mut file, &mut outfile).unwrap(); + } + + let file_path = fs::canonicalize(file_path.clone())?; + unpacked_files.push(file_path); + } + + Ok(unpacked_files) } } @@ -30,6 +77,6 @@ impl Decompressor for ZipDecompressor { let files_unpacked = Self::unpack_files(&from.path, destination_path)?; // placeholder return - Err(error::Error::IOError) + Ok(files_unpacked) } } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 80d4c1b..16bac57 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,43 +10,64 @@ pub enum Error { InvalidUnicode, InvalidInput, IOError, + FileNotFound, + AlreadyExists, + InvalidZipArchive(&'static str), + PermissionDenied, + UnsupportedZipArchive(&'static str), InputsMustHaveBeenDecompressible(String), } -// This should be placed somewhere else pub type OuchResult = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; match self { - InvalidInput => write!( + Error::InvalidInput => write!( f, "When `-o/--output` is omitted, all input files should be compressed files." ), Error::MissingExtensionError(filename) => { write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename) - } + }, Error::InputsMustHaveBeenDecompressible(file) => { write!(f, "file '{}' is not decompressible", file.red()) + }, + // TODO: find out a way to attach the missing file in question here + Error::FileNotFound => { + write!(f, "file not found!") } - _ => { + err => { // TODO - write!(f, "todo: missing description for error") + write!(f, "todo: missing description for error {:?}", err) } } } } - impl From for Error { fn from(err: std::io::Error) -> Self { - // Ideally I'd store `err` as a variant of ouch's Error - // but I need Error to have Eq, which std::io::Error does not - // implement. - println!("{}: {:#?}", "error".red(), err); + match err.kind() { + std::io::ErrorKind::NotFound => Self::FileNotFound, + std::io::ErrorKind::PermissionDenied => Self::PermissionDenied, + std::io::ErrorKind::AlreadyExists => Self::AlreadyExists, + _other => { + println!("{}: {:#?}", "IO error".red(), err); + Self::IOError + } + } + } +} - Self::IOError +impl From for Error { + fn from(err: zip::result::ZipError) -> Self { + use zip::result::ZipError::*; + match err { + Io(io_err) => Self::from(io_err), + InvalidArchive(filename) => Self::InvalidZipArchive(filename), + FileNotFound => Self::FileNotFound, + UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename) + } } } \ No newline at end of file diff --git a/src/evaluator.rs b/src/evaluator.rs index b992705..bdb6d44 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -26,13 +26,13 @@ impl Evaluator { return Err(error::Error::InvalidInput); } let extension = file.extension.clone().unwrap(); - let decompressor = match extension.second_ext { + let decompressor: Box = match extension.second_ext { CompressionFormat::Tar => { Box::new(TarDecompressor{}) }, - // CompressionFormat::Zip => { - // Box::new(ZipDecompressor{}) - // } + CompressionFormat::Zip => { + Box::new(ZipDecompressor{}) + } _ => { todo!() } @@ -43,7 +43,6 @@ impl Evaluator { } fn decompress_file(&self, file: &File) -> error::OuchResult<()> { - println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), file.path); let output_file = &self.command.output; let decompressor = self.get_decompressor(file)?; let files_unpacked = decompressor.decompress(file, output_file)?; diff --git a/src/main.rs b/src/main.rs index 2b2bac5..ab67756 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,14 +14,20 @@ mod decompressors; use error::OuchResult; fn main() -> OuchResult<()>{ + let print_error = |err| { + println!("{}: {}", "error".red(), err); + }; let matches = cli::get_matches(); match cli::Command::try_from(matches) { Ok(command) => { let mut eval = evaluator::Evaluator::new(command); - eval.evaluate()?; + match eval.evaluate() { + Ok(_) => {}, + Err(err) => print_error(err) + } } Err(err) => { - print!("{}: {}\n", "error".red(), err); + print_error(err) } } Ok(()) diff --git a/src/utils.rs b/src/utils.rs index 82babb9..e6d1d1f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -use std::{fs, path::Path}; +use std::{fs, path::{Component, Path, PathBuf}}; use colored::Colorize; use crate::{error::OuchResult, file::File};