commit 08489b028c8e85176bf5d55576f75c1817df9019 Author: Vinícius Rodrigues Miguel Date: Fri Mar 19 04:40:19 2021 -0300 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1a13baa --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,416 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bgzip" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adff113e9fe73a6d0c4efd0f143857bd6bdad40743542f3f57464398c532234f" +dependencies = [ + "failure", + "flate2", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bzip2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf8012c8a15d5df745fcf258d93e6149dcf102882c8d8702d9cff778eab43a8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.10+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17fa3d1ac1ca21c5c4e36a97f3c3eb25084576f6fc47bf0139c1123434216c6c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum_primitive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" +dependencies = [ + "num-traits 0.1.43", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" + +[[package]] +name = "lzma-sys" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "niffler" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea40fb13399dd0e9780ea3d82cb6cf27f489f63d820aa7dc9ec967750dc6d58" +dependencies = [ + "bgzip", + "bzip2", + "cfg-if", + "enum_primitive", + "flate2", + "thiserror", + "xz2", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + +[[package]] +name = "ouch" +version = "0.1.0" +dependencies = [ + "clap", + "colored", + "niffler", +] + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xz2" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" +dependencies = [ + "lzma-sys", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a901259 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ouch" +version = "0.1.0" +authors = ["Vinícius Rodrigues Miguel "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +colored = "2.0.0" +niffler = "2.3.1" +clap = "2.33.3" diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1c50bf --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# ouch + +`ouch` is the Obvious Unified Compression (and decompression) Helper. + +## How does it work? + +`ouch` infers commands from the extensions of its command-line options. + +``` +ouch 0.1.0 +ouch is a unified compression & decompression utility + +USAGE: + ouch [OPTIONS] --input ... + +FLAGS: + -h, --help Displays this message and exits + -V, --version Prints version information + +OPTIONS: + -i, --input ... Input files (TODO description) + -o, --output Output file (TODO description) +``` + +### Examples + +#### Decompressing a bunch of files + +```bash +$ ouch -i file{1..5}.zip +info: attempting to decompress input files into single_folder +info: done! +``` + +When no output file is supplied, `ouch` infers that it must decompress all of its input files. This will error if any of the input files are not decompressable. + +#### Decompressing a bunch of files into a folder + +```bash +$ ouch -i file{1..5}.tar.gz -o some-folder +info: attempting to decompress input files into single_folder +info: done! +``` + +When the output file is not a compressed file, `ouch` will check if all input files are decompressable and infer that it must decompress them into the output file. + +#### Compressing files + +```bash +$ ouch -i file{1..20} -o archive.tar +info: trying to compress input files into 'archive.tar' +info: done! +``` + +### Error scenarios + +#### No clear decompression algorithm + +```bash +$ ouch -i some-file -o some-folder +error: file 'some-file' is not decompressable. +``` + +`ouch` might (TODO!) be able to sniff a file's compression format if it isn't supplied in the future, but that is not currently implemented. + + + diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..e6f9e9d --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,218 @@ +use std::{convert::TryFrom, ffi::OsStr, path::PathBuf, vec::Vec}; + +use clap::{Arg}; +use colored::Colorize; + +use crate::error; +use crate::extensions::CompressionExtension; +use crate::file::File; + +#[derive(Debug)] +pub enum CommandType { + Compression( + // Files to be compressed + Vec, + ), + Decompression( + // Files to be decompressed and their extensions + Vec<(PathBuf, CompressionExtension)>, + ), +} + +#[derive(Debug)] +pub struct Command { + pub command_type: CommandType, + pub output: Option, +} + +pub fn get_matches() -> clap::ArgMatches<'static> { + clap::App::new("ouch") + .version("0.1.0") + .about("ouch is a unified compression & decompression utility") + .help_message("Displays this message and exits") + .settings(&[ + clap::AppSettings::ColoredHelp, + clap::AppSettings::ArgRequiredElseHelp, + ]) + .arg( + Arg::with_name("input") + .required(true) + .multiple(true) + .long("input") + .short("i") + .help("Input files (TODO description)") + .takes_value(true), + ) + .arg( + Arg::with_name("output") + // --output/-o not required when output can be inferred from the input files + .required(false) + .multiple(false) + .long("output") + .short("o") + .help("Output file (TODO description)") + .takes_value(true), + ) + .get_matches() +} + +// holy spaghetti code +impl TryFrom> for Command { + + type Error = error::Error; + + fn try_from(matches: clap::ArgMatches<'static>) -> error::OuchResult { + // Possibilities: + // * Case 1: output not supplied, therefore try to infer output by checking if all input files are decompressable + // * Case 2: output supplied + + let output_was_supplied = matches.is_present("output"); + + if output_was_supplied { + let output_file = matches + .value_of("output") + .unwrap(); // Safe unwrap since we've established that output was supplied + + let input_files = matches + .values_of("input") + .unwrap(); // Safe to unwrap since input is an obligatory argument + // .map(PathBuf::from) + // .collect(); + + + let output_file_extension = CompressionExtension::try_from(output_file); + let output_is_compressable = output_file_extension.is_ok(); + if output_is_compressable { + println!("{}: trying to compress input files into '{}'", "info".yellow(), output_file); + + let input_files = input_files.map(PathBuf::from).collect(); + + return Ok( + Command { + command_type: CommandType::Compression(input_files), + output: Some(File::WithExtension( + (output_file.into(), output_file_extension.unwrap()) + )) + } + ); + + } + else { + // Checking if input files are decompressable + let input_files = input_files + .map(|filename| (filename, CompressionExtension::try_from(filename))); + + for file in input_files.clone() { + if let (file, Err(_)) = file { + eprintln!("{}: file '{}' is not decompressable.", "error".red(), file); + return Err(error::Error::InputsMustHaveBeenDecompressable(file.into())); + } + } + + let input_files = + input_files + .map(|(filename, extension)| + (PathBuf::from(filename), extension.unwrap()) + ) + .collect(); + + println!("{}: attempting to decompress input files into {}", "info".yellow(), output_file); + return Ok( + Command { + command_type: CommandType::Decompression(input_files), + output: Some(File::WithoutExtension(output_file.into())) + } + ); + } + } else { + // BIG TODO + Err(error::Error::MissingExtensionError("placeholder result".into())) + } + } +} + +// impl TryFrom> for ArgValues { +// type Error = error::OuchError; +// fn try_from(matches: clap::ArgMatches<'static>) -> error::OuchResult { +// // Case 1: -o was set +// // Case 1.1: -o was set and has a (supported) compression file extension +// // |--> Compress all input files into the supplied output file (no extension checks on inputs) +// // Case 1.2: -o was set and is not a supported expression +// // Case 2: -o was not set +// // Case 2.1: -o was not set and all input files are (supported) compression file extensions +// // |--> Decompress input files into inferred filenames or directories +// // Case 2.2: -o was not set and not all input files are (supported) compression file extensions +// // |--> Issue an error + +// let inputs = matches +// .values_of("input") +// .unwrap() // Safe to unwrap since this is a required argument +// .map(|input: &str| { +// ( +// PathBuf::from(input), +// CompressionExtension::try_from(input).ok(), +// ) +// }); + +// let output_was_supplied = matches.is_present("output"); +// let inputs_are_compressed_files = inputs.clone().all(|(_, ext)| ext.is_some()); + +// match (output_was_supplied, inputs_are_compressed_files) { +// (true, true) => { +// // -o was set and inputs are all valid compressed files + +// let output = matches.value_of("output").unwrap(); +// let output = PathBuf::from(output); +// match CompressionExtension::try_from(&output) { +// Ok(ext) => { +// // If the output file is a valid compressed file, then we compress the input files into it +// Ok(Self { +// command_type: CommandType::Compress( +// inputs.map(|(path, _)| path).collect(), +// ), +// output: Some((output, ext)), +// }) +// } +// Err(_) => { +// // If the output file is not a compressed file, then we decompress the input files into it +// Ok(Self { +// command_type: CommandType::Decompress( +// inputs.map(|(path, ext)| (path, ext.unwrap())).collect(), +// ), +// output: Some((output, CompressionExtension::NotCompressed)), +// }) +// } +// } +// } +// (true, false) => { +// // -o was set and inputs are not (all) valid compressed files +// let output_str = matches.value_of("output").unwrap(); +// let output = PathBuf::from(output_str); +// let output_ext = match CompressionExtension::try_from(&output) { +// Ok(ext) => ext, +// Err(_) => { +// return Err(error::OuchError::MissingExtensionError(output_str.into())); +// } +// }; + +// Ok(Self { +// command_type: CommandType::Compress(inputs.map(|(path, _)| path).collect()), +// output: Some((output, output_ext)), +// }) +// } +// (false, true) => { +// // Case 2.1: -o was not set and all input files are (supported) compression file extensions +// Ok(Self { +// command_type: CommandType::Decompress( +// inputs.map(|(path, ext)| (path, ext.unwrap())).collect(), +// ), +// output: None, +// }) +// } +// (false, false) => { +// // Case 2.2: -o was not set and not all input files are not (supported) compression file extensions +// Err(error::OuchError::InvalidInput) +// } +// } +// } +// } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..9cf1b47 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,38 @@ +use std::{fmt, path::PathBuf}; + +use colored::Colorize; + +#[derive(Debug)] +pub enum Error { + UnknownExtensionError(String), + MissingExtensionError(String), + InvalidUnicode, + InvalidInput, + InputsMustHaveBeenDecompressable(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!( + 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::InputsMustHaveBeenDecompressable(file) => { + write!(f, "file '{}' is not decompressable", file.red()) + } + _ => { + // TODO + write!(f, "") + } + } + } +} diff --git a/src/extensions.rs b/src/extensions.rs new file mode 100644 index 0000000..dc85008 --- /dev/null +++ b/src/extensions.rs @@ -0,0 +1,74 @@ +use std::{ + convert::TryFrom, + path::{Path, PathBuf}, +}; + +use crate::error; +#[derive(Debug)] +/// Accepted extensions for input and output +pub enum CompressionExtension { + // .gz + Gzip, + // .bz + Bzip, + // .lzma + Lzma, + // .tar (technically not a compression extension, but will do for now) + Tar, + // .zip + Zip, + // Not a supported compressed file extension (any other file) + // TODO: it makes no sense for this variant to exist here + // NotCompressed +} + +impl TryFrom<&PathBuf> for CompressionExtension { + type Error = error::Error; + + fn try_from(ext: &PathBuf) -> Result { + use CompressionExtension::*; + + let ext = match ext.extension() { + Some(ext) => ext, + None => { + return Err(error::Error::MissingExtensionError(String::new())); + } + }; + + let ext = match ext.to_str() { + Some(str) => str, + None => return Err(error::Error::InvalidUnicode), + }; + + match ext { + "zip" => Ok(Zip), + "tar" => Ok(Tar), + other => Err(error::Error::UnknownExtensionError(other.into())), + } + } +} + +impl TryFrom<&str> for CompressionExtension { + type Error = error::Error; + + fn try_from(filename: &str) -> Result { + use CompressionExtension::*; + + let filename = Path::new(filename); + let ext = match filename.extension() { + Some(ext) => ext, + None => return Err(error::Error::MissingExtensionError(String::new())), + }; + + let ext = match ext.to_str() { + Some(str) => str, + None => return Err(error::Error::InvalidUnicode), + }; + + match ext { + "zip" => Ok(Zip), + "tar" => Ok(Tar), + other => Err(error::Error::UnknownExtensionError(other.into())), + } + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..325e8ba --- /dev/null +++ b/src/file.rs @@ -0,0 +1,43 @@ +use std::{convert::TryFrom, path::PathBuf, str::FromStr}; + +use crate::error::Error as OuchError; +use crate::error; +use crate::extensions::CompressionExtension; + +// pub type File = (PathBuf, CompressionExtension); + +// #[derive(Debug)] +// pub struct FileWithExtension { +// pub extension: CompressionExtension, +// pub filename: PathBuf, +// } + +#[derive(Debug)] +pub enum File { + WithExtension((PathBuf, CompressionExtension)), + WithoutExtension(PathBuf) +} + +// impl TryFrom for FileWithExtension { +// type Error = OuchError; + +// fn try_from(filename: String) -> error::OuchResult { +// // Safe to unwrap (infallible operation) +// let filename = PathBuf::from_str(&filename).unwrap(); + +// let os_str = match filename.extension() { +// Some(os_str) => os_str, +// None => return Err(OuchError::MissingExtensionError(filename.to_string_lossy().to_string())), +// }; + +// let extension = match CompressionExtension::try_from(os_str.into()) { +// Ok(ext) => ext, +// Err(err) => return Err(err), +// }; + +// Ok(Self { +// filename, +// extension, +// }) +// } +// } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..34e9a50 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,45 @@ +use std::{convert::TryFrom, fs::File}; + +use cli::get_matches; + +mod cli; +mod file; +mod extensions; +mod error; + +fn main() { + + // Just testing + + // let args: Vec = std::env::args().collect(); + + // let file = std::fs::read(args[1].clone()).unwrap(); + + // match niffler::sniff(Box::new(&file[..])) { + // Ok((reader, compression)) => {}, + // Err(err) => {} + // } + + // let (mut reader, compression) = niffler::sniff(Box::new(&file[..])).unwrap(); + + // match compression { + // niffler::Format::Gzip => {} + // niffler::Format::Bzip => {} + // niffler::Format::Lzma => {} + // niffler::Format::No => {} + // } + + // let mut contents = String::new(); + // println!("Contents: {}", reader.read_to_string(&mut contents).unwrap()); + + // dbg!(compression); + + let matches = get_matches(); + match cli::Command::try_from(matches) { + Ok(vals) => { dbg!(vals); }, + Err(err) => { + print!("{}\n", err); + } + } + +} diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..e69de29