diff --git a/Cargo.lock b/Cargo.lock index 1a13baa..1b5fa10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ansi_term" version = "0.11.0" @@ -48,9 +54,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.4.4", "object", "rustc-demangle", ] @@ -71,6 +77,22 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +dependencies = [ + "bzip2-sys", + "libc", +] + [[package]] name = "bzip2" version = "0.4.2" @@ -98,6 +120,12 @@ version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -136,7 +164,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -171,15 +199,27 @@ dependencies = [ ] [[package]] -name = "flate2" -version = "1.0.20" +name = "filetime" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +dependencies = [ + "cfg-if 0.1.10", "crc32fast", "libc", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -220,6 +260,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -237,8 +286,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ea40fb13399dd0e9780ea3d82cb6cf27f489f63d820aa7dc9ec967750dc6d58" dependencies = [ "bgzip", - "bzip2", - "cfg-if", + "bzip2 0.4.2", + "cfg-if 1.0.0", "enum_primitive", "flate2", "thiserror", @@ -276,6 +325,8 @@ dependencies = [ "clap", "colored", "niffler", + "tar", + "zip", ] [[package]] @@ -302,6 +353,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + [[package]] name = "rustc-demangle" version = "0.1.18" @@ -337,6 +397,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tar" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0bcfbd6a598361fda270d82469fff3d65089dc33e175c9a131f7b4cd395f228" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -366,6 +437,17 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -384,6 +466,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "winapi" version = "0.3.9" @@ -406,6 +494,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +dependencies = [ + "libc", +] + [[package]] name = "xz2" version = "0.1.6" @@ -414,3 +511,17 @@ checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" dependencies = [ "lzma-sys", ] + +[[package]] +name = "zip" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8264fcea9b7a036a4a5103d7153e988dbc2ebbafb34f68a3c2d404b6b82d74b6" +dependencies = [ + "byteorder", + "bzip2 0.3.3", + "crc32fast", + "flate2", + "thiserror", + "time", +] diff --git a/Cargo.toml b/Cargo.toml index a901259..d905538 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,5 @@ edition = "2018" colored = "2.0.0" niffler = "2.3.1" clap = "2.33.3" +zip = "0.5.11" +tar = "0.4.33" \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 0b595a7..6571c55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -124,12 +124,6 @@ impl TryFrom> for Command { let input_files = process_decompressible_input(input_files)?; - println!( - "{}: attempting to decompress input files into {}", - "info".yellow(), - output_file - ); - let input_files = input_files.into_iter().map(File::from).collect(); return Ok(Command { diff --git a/src/decompressors/decompressor.rs b/src/decompressors/decompressor.rs new file mode 100644 index 0000000..20b1051 --- /dev/null +++ b/src/decompressors/decompressor.rs @@ -0,0 +1 @@ +/// This file should/could store a Decompressor trait \ No newline at end of file diff --git a/src/decompressors/mod.rs b/src/decompressors/mod.rs new file mode 100644 index 0000000..8e97713 --- /dev/null +++ b/src/decompressors/mod.rs @@ -0,0 +1 @@ +pub mod tar; \ No newline at end of file diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs new file mode 100644 index 0000000..b2b1465 --- /dev/null +++ b/src/decompressors/tar.rs @@ -0,0 +1,33 @@ +use std::{fs, path::{Path, PathBuf}}; + +use crate::file::File; +use crate::error::OuchResult; + +use colored::Colorize; +pub struct Decompressor {} + +impl Decompressor { + pub fn decompress(from: &File, into: &Option) -> OuchResult<()> { + let destination_path = match into { + Some(output) => { + // Must be None according to the way command-line arg. parsing in Ouch works + assert_eq!(output.extension, None); + + Path::new(&output.path) + } + None => Path::new(".") + }; + + if !destination_path.exists() { + println!("{}: attempting to create folder {:?}.", "info".yellow(), &destination_path); + std::fs::create_dir_all(destination_path)?; + println!("{}: directory {:#?} created.", "info".yellow(), fs::canonicalize(&destination_path)?); + } + + + + + + Ok(()) + } +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 2ca7502..80d4c1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ pub enum Error { // TODO: get rid of this error variant InvalidUnicode, InvalidInput, + IOError, InputsMustHaveBeenDecompressible(String), } @@ -32,8 +33,20 @@ impl fmt::Display for Error { } _ => { // TODO - write!(f, "") + write!(f, "todo: missing description for error") } } } } + + +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); + + Self::IOError + } +} \ No newline at end of file diff --git a/src/evaluator.rs b/src/evaluator.rs index 1f5eea1..797a158 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,8 +1,8 @@ -use std::{convert::TryFrom, path::{PathBuf}}; use colored::Colorize; use crate::{cli::{Command, CommandKind}, error, extension::CompressionFormat, file::File}; +use crate::decompressors::tar; pub struct Evaluator { command: Command, @@ -10,53 +10,48 @@ pub struct Evaluator { } impl Evaluator { + // todo: remove this? pub fn new(command: Command) -> Self { Self { command } } - fn handle_compression(files_to_compress: &[PathBuf], output_file: &Option) { - - } - - fn decompress_file(mut filename: &PathBuf, mut extension: CompressionFormat, output_file: &Option) -> error::OuchResult<()> { - loop { - println!("{}: attempting to decompress '{:?}'", "ouch".bright_blue(), filename); + fn decompress_file(&self, file: &File) -> error::OuchResult<()> { + println!("{}: attempting to decompress {:?}", "ouch".bright_blue(), file.path); + if file.extension.is_none() { + // This block *should* be unreachable + eprintln!("{}: reached Evaluator::decompress_file without known extension.", "internal error".red()); + return Err(error::Error::InvalidInput); } + let extension = file.extension.clone().unwrap(); + let output_file = &self.command.output; + + match extension.second_ext { + CompressionFormat::Tar => tar::Decompressor::decompress(file, output_file)?, + _ => { + todo!() + } + } + + // TODO: decompress first extension + + Ok(()) } - fn handle_decompression(files_to_decompress: &[File], output_file: &Option) { - // for (filename, extension) in files_to_decompress { - // // println!("file: {:?}, extension: {:?}", filename, extension); - - // // TODO: actually decompress anything ;-; - - // // Once decompressed, check if the file can be decompressed further - // // e.g.: "foobar.tar.gz" -> "foobar.tar" - - - - // let filename: &PathBuf = &filename.as_path().file_stem().unwrap().into(); - // match CompressionFormat::try_from(filename) { - // Ok(extension) => { - // println!("{}: attempting to decompress {:?}, ext: {:?}", "info".yellow(), filename, extension); - // }, - // Err(err) => { - // continue; - // } - // } - // } - } - - pub fn evaluate(&mut self) { + pub fn evaluate(&mut self) -> error::OuchResult<()> { match &self.command.kind { CommandKind::Compression(files_to_compress) => { - Evaluator::handle_compression(files_to_compress, &self.command.output); + for _file in files_to_compress { + todo!(); + } } CommandKind::Decompression(files_to_decompress) => { - Evaluator::handle_decompression(files_to_decompress, &self.command.output); + for file in files_to_decompress { + self.decompress_file(file)?; + } } } + Ok(()) } } \ No newline at end of file diff --git a/src/extension.rs b/src/extension.rs index ff22cf1..a279041 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -6,10 +6,10 @@ use CompressionFormat::*; /// Represents the extension of a file, but only really caring about /// compression formats (and .tar). /// Ex.: Extension::new("file.tar.gz") == Extension { first_ext: Some(Tar), second_ext: Gzip } -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Extension { - first_ext: Option, - second_ext: CompressionFormat + pub first_ext: Option, + pub second_ext: CompressionFormat } impl From for Extension { @@ -86,7 +86,7 @@ pub fn get_extension_from_filename(filename: &str) -> Option<(&str, &str)> { } } -#[derive(PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] /// Accepted extensions for input and output pub enum CompressionFormat { // .gz diff --git a/src/main.rs b/src/main.rs index 8977925..0674463 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ -use std::{convert::TryFrom}; +use std::{convert::TryFrom, fs::File, path::{Path, PathBuf}}; use colored::Colorize; +use tar::Archive; mod cli; mod error; @@ -9,15 +10,36 @@ mod file; mod test; mod evaluator; +mod decompressors; + fn main() { - let matches = cli::get_matches(); - match cli::Command::try_from(matches) { - Ok(command) => { - let mut eval = evaluator::Evaluator::new(command); - eval.evaluate(); - } - Err(err) => { - print!("{}: {}\n", "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(); + // } + // Err(err) => { + // print!("{}: {}\n", "error".red(), err); + // } + // } + + + // Testing tar unarchival + let file = File::open("ouch.tar").unwrap(); + let mut a = Archive::new(file); + + for file in a.entries().unwrap() { + // Make sure there wasn't an I/O error + let mut file = file.unwrap(); + + let path = if let Ok(path) = file.path() { + path + } else { + continue; + }; + let name = path.to_string_lossy(); + + file.unpack(format!("{}", name)).unwrap(); } }