decompressors.zip: now working

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-21 19:11:27 -03:00
parent 0a81384dd8
commit e705024c61
5 changed files with 100 additions and 27 deletions

View File

@ -1,10 +1,7 @@
use std::{ use std::{fs, io, path::{Path, PathBuf}};
fs,
path::{Path, PathBuf},
};
use colored::Colorize; use colored::Colorize;
use zip; use zip::{self, read::ZipFile};
use crate::{error::{self, OuchResult}, utils}; use crate::{error::{self, OuchResult}, utils};
use crate::file::File; use crate::file::File;
@ -14,9 +11,59 @@ use super::decompressor::Decompressor;
pub struct ZipDecompressor {} pub struct ZipDecompressor {}
impl 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<Vec<PathBuf>> { fn unpack_files(from: &Path, into: &Path) -> OuchResult<Vec<PathBuf>> {
let mut unpacked_files = vec![];
// placeholder return // 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)?; let files_unpacked = Self::unpack_files(&from.path, destination_path)?;
// placeholder return // placeholder return
Err(error::Error::IOError) Ok(files_unpacked)
} }
} }

View File

@ -10,43 +10,64 @@ pub enum Error {
InvalidUnicode, InvalidUnicode,
InvalidInput, InvalidInput,
IOError, IOError,
FileNotFound,
AlreadyExists,
InvalidZipArchive(&'static str),
PermissionDenied,
UnsupportedZipArchive(&'static str),
InputsMustHaveBeenDecompressible(String), InputsMustHaveBeenDecompressible(String),
} }
// This should be placed somewhere else
pub type OuchResult<T> = Result<T, Error>; pub type OuchResult<T> = Result<T, Error>;
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self { match self {
InvalidInput => write!( Error::InvalidInput => write!(
f, f,
"When `-o/--output` is omitted, all input files should be compressed files." "When `-o/--output` is omitted, all input files should be compressed files."
), ),
Error::MissingExtensionError(filename) => { Error::MissingExtensionError(filename) => {
write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename) write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename)
} },
Error::InputsMustHaveBeenDecompressible(file) => { Error::InputsMustHaveBeenDecompressible(file) => {
write!(f, "file '{}' is not decompressible", file.red()) 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 // TODO
write!(f, "todo: missing description for error") write!(f, "todo: missing description for error {:?}", err)
} }
} }
} }
} }
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self { fn from(err: std::io::Error) -> Self {
// Ideally I'd store `err` as a variant of ouch's Error match err.kind() {
// but I need Error to have Eq, which std::io::Error does not std::io::ErrorKind::NotFound => Self::FileNotFound,
// implement. std::io::ErrorKind::PermissionDenied => Self::PermissionDenied,
println!("{}: {:#?}", "error".red(), err); std::io::ErrorKind::AlreadyExists => Self::AlreadyExists,
_other => {
println!("{}: {:#?}", "IO error".red(), err);
Self::IOError Self::IOError
} }
} }
}
}
impl From<zip::result::ZipError> 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)
}
}
}

View File

@ -26,13 +26,13 @@ impl Evaluator {
return Err(error::Error::InvalidInput); return Err(error::Error::InvalidInput);
} }
let extension = file.extension.clone().unwrap(); let extension = file.extension.clone().unwrap();
let decompressor = match extension.second_ext { let decompressor: Box<dyn Decompressor> = match extension.second_ext {
CompressionFormat::Tar => { CompressionFormat::Tar => {
Box::new(TarDecompressor{}) Box::new(TarDecompressor{})
}, },
// CompressionFormat::Zip => { CompressionFormat::Zip => {
// Box::new(ZipDecompressor{}) Box::new(ZipDecompressor{})
// } }
_ => { _ => {
todo!() todo!()
} }
@ -43,7 +43,6 @@ impl Evaluator {
} }
fn decompress_file(&self, file: &File) -> error::OuchResult<()> { fn decompress_file(&self, file: &File) -> error::OuchResult<()> {
println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), file.path);
let output_file = &self.command.output; let output_file = &self.command.output;
let decompressor = self.get_decompressor(file)?; let decompressor = self.get_decompressor(file)?;
let files_unpacked = decompressor.decompress(file, output_file)?; let files_unpacked = decompressor.decompress(file, output_file)?;

View File

@ -14,14 +14,20 @@ mod decompressors;
use error::OuchResult; use error::OuchResult;
fn main() -> OuchResult<()>{ fn main() -> OuchResult<()>{
let print_error = |err| {
println!("{}: {}", "error".red(), err);
};
let matches = cli::get_matches(); let matches = cli::get_matches();
match cli::Command::try_from(matches) { match cli::Command::try_from(matches) {
Ok(command) => { Ok(command) => {
let mut eval = evaluator::Evaluator::new(command); let mut eval = evaluator::Evaluator::new(command);
eval.evaluate()?; match eval.evaluate() {
Ok(_) => {},
Err(err) => print_error(err)
}
} }
Err(err) => { Err(err) => {
print!("{}: {}\n", "error".red(), err); print_error(err)
} }
} }
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use std::{fs, path::Path}; use std::{fs, path::{Component, Path, PathBuf}};
use colored::Colorize; use colored::Colorize;
use crate::{error::OuchResult, file::File}; use crate::{error::OuchResult, file::File};