Skip to content

Commit

Permalink
Merge pull request #24 from vrmiguel/testing-compression
Browse files Browse the repository at this point in the history
Testing compression and decompression of formats that support multiple files
  • Loading branch information
vrmiguel authored May 17, 2021
2 parents 8548f73 + f4e1798 commit afbda44
Show file tree
Hide file tree
Showing 22 changed files with 269 additions and 546 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ tar = "0.4.33"
xz2 = "0.1.6"
zip = "0.5.11"


# Dependency from workspace
oof = { path = "./oof" }

[dev-dependencies]
tempdir = "0.3.7"
rand = { version = "0.8.3", default-features = false, features = ["small_rng", "std"] }

[profile.release]
lto = true
codegen-units = 1
Expand Down
2 changes: 1 addition & 1 deletion oof/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub enum OofError<'t> {
UnknownLongFlag(String),
MisplacedShortArgFlagError(char),
MissingValueToFlag(&'t Flag),
DuplicatedFlag(&'t Flag)
DuplicatedFlag(&'t Flag),
}

impl<'t> error::Error for OofError<'t> {
Expand Down
12 changes: 2 additions & 10 deletions oof/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ pub struct ArgFlag;

impl ArgFlag {
pub fn long(name: &'static str) -> Flag {
Flag {
long: name,
short: None,
takes_value: true,
}
Flag { long: name, short: None, takes_value: true }
}
}

Expand All @@ -40,11 +36,7 @@ impl std::fmt::Display for Flag {

impl Flag {
pub fn long(name: &'static str) -> Self {
Self {
long: name,
short: None,
takes_value: false,
}
Self { long: name, short: None, takes_value: false }
}

pub fn short(mut self, short_flag_char: char) -> Self {
Expand Down
56 changes: 21 additions & 35 deletions oof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ mod error;
mod flags;
pub mod util;

use std::{collections::BTreeMap, ffi::{OsStr, OsString}};
use std::{
collections::BTreeMap,
ffi::{OsStr, OsString},
};

pub use error::OofError;
pub use flags::{ArgFlag, Flag, FlagType, Flags};
Expand Down Expand Up @@ -103,9 +106,9 @@ pub fn filter_flags(
}

// If it is a flag, now we try to interpret it as valid utf-8
let flag= match arg.to_str() {
let flag = match arg.to_str() {
Some(arg) => arg,
None => return Err(OofError::InvalidUnicode(arg))
None => return Err(OofError::InvalidUnicode(arg)),
};

// Only one hyphen in the flag
Expand All @@ -127,12 +130,11 @@ pub fn filter_flags(
// Safety: this loop only runs when len >= 1, so this subtraction is safe
let is_last_letter = i == letters.len() - 1;

let flag_info = short_flags_info.get(&letter).ok_or(
OofError::UnknownShortFlag(letter)
)?;
let flag_info =
short_flags_info.get(&letter).ok_or(OofError::UnknownShortFlag(letter))?;

if !is_last_letter && flag_info.takes_value {
return Err(OofError::MisplacedShortArgFlagError(letter))
return Err(OofError::MisplacedShortArgFlagError(letter));
// Because "-AB argument" only works if B takes values, not A.
// That is, the short flag that takes values needs to come at the end
// of this piece of text
Expand All @@ -147,9 +149,8 @@ pub fn filter_flags(
}

// pop the next one
let flag_argument = iter.next().ok_or(
OofError::MissingValueToFlag(flag_info)
)?;
let flag_argument =
iter.next().ok_or(OofError::MissingValueToFlag(flag_info))?;

// Otherwise, insert it.
result_flags.argument_flags.insert(flag_name, flag_argument);
Expand All @@ -167,9 +168,9 @@ pub fn filter_flags(
if let FlagType::Long = flag_type {
let flag = trim_double_hyphen(flag);

let flag_info = long_flags_info.get(flag).ok_or_else(|| {
OofError::UnknownLongFlag(String::from(flag))
})?;
let flag_info = long_flags_info
.get(flag)
.ok_or_else(|| OofError::UnknownLongFlag(String::from(flag)))?;

let flag_name = flag_info.long;

Expand All @@ -179,9 +180,7 @@ pub fn filter_flags(
return Err(OofError::DuplicatedFlag(flag_info));
}

let flag_argument = iter.next().ok_or(
OofError::MissingValueToFlag(flag_info)
)?;
let flag_argument = iter.next().ok_or(OofError::MissingValueToFlag(flag_info))?;
result_flags.argument_flags.insert(flag_name, flag_argument);
} else {
// If it was already inserted
Expand Down Expand Up @@ -209,9 +208,7 @@ where
T: AsRef<OsStr>,
U: AsRef<str>,
{
texts
.iter()
.any(|text| args.iter().any(|arg| arg.as_ref() == text.as_ref()))
texts.iter().any(|text| args.iter().any(|arg| arg.as_ref() == text.as_ref()))
}

#[cfg(test)]
Expand All @@ -237,14 +234,8 @@ mod tests {

assert_eq!(args, gen_args("ouch a.zip b.tar.gz c.tar"));
assert!(flags.is_present("output_file"));
assert_eq!(
Some(&OsString::from("new_folder")),
flags.arg("output_file")
);
assert_eq!(
Some(OsString::from("new_folder")),
flags.take_arg("output_file")
);
assert_eq!(Some(&OsString::from("new_folder")), flags.arg("output_file"));
assert_eq!(Some(OsString::from("new_folder")), flags.take_arg("output_file"));
assert!(!flags.is_present("output_file"));
}

Expand Down Expand Up @@ -291,10 +282,7 @@ mod tests {
// TODO: remove should_panic and use proper error handling inside of filter_args
#[should_panic]
fn test_flag_info_with_long_flag_conflict() {
let flags_info = [
ArgFlag::long("verbose").short('a'),
Flag::long("verbose").short('b'),
];
let flags_info = [ArgFlag::long("verbose").short('a'), Flag::long("verbose").short('b')];

// Should panic here
let result = filter_flags(vec![], &flags_info);
Expand All @@ -305,10 +293,8 @@ mod tests {
// TODO: remove should_panic and use proper error handling inside of filter_args
#[should_panic]
fn test_flag_info_with_short_flag_conflict() {
let flags_info = [
ArgFlag::long("output_file").short('o'),
Flag::long("verbose").short('o'),
];
let flags_info =
[ArgFlag::long("output_file").short('o'), Flag::long("verbose").short('o')];

// Should panic here
filter_flags(vec![], &flags_info).unwrap_err();
Expand Down
13 changes: 13 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Normal features
max_width = 100
imports_granularity = "Crate"
match_block_trailing_comma = true
overflow_delimited_expr = true
reorder_impl_items = true
use_field_init_shorthand = true
newline_style = "Unix"
edition = "2018"
reorder_imports = true
reorder_modules = true
use_try_shorthand = true
use_small_heuristics = "Max"
54 changes: 17 additions & 37 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ use std::{
vec::Vec,
};

use strsim::normalized_damerau_levenshtein;
use oof::{arg_flag, flag};

use strsim::normalized_damerau_levenshtein;

#[derive(PartialEq, Eq, Debug)]
pub enum Command {
Expand Down Expand Up @@ -36,11 +35,10 @@ pub struct ParsedArgs {
pub flags: oof::Flags,
}


/// check_for_typo checks if the first argument is
/// a typo for the compress subcommand.
/// check_for_typo checks if the first argument is
/// a typo for the compress subcommand.
/// Returns true if the arg is probably a typo or false otherwise.
fn is_typo<'a, P>(path: P) -> bool
fn is_typo<'a, P>(path: P) -> bool
where
P: AsRef<Path> + 'a,
{
Expand All @@ -66,12 +64,10 @@ where
} else {
Err(crate::Error::IoError(io_err))
}
}
},
}
}



fn canonicalize_files<'a, P>(files: Vec<P>) -> crate::Result<Vec<PathBuf>>
where
P: AsRef<Path> + 'a,
Expand All @@ -81,22 +77,14 @@ where

pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
if oof::matches_any_arg(&args, &["--help", "-h"]) || args.is_empty() {
return Ok(ParsedArgs {
command: Command::ShowHelp,
flags: oof::Flags::default(),
});
return Ok(ParsedArgs { command: Command::ShowHelp, flags: oof::Flags::default() });
}

if oof::matches_any_arg(&args, &["--version"]) {
return Ok(ParsedArgs {
command: Command::ShowVersion,
flags: oof::Flags::default(),
});
return Ok(ParsedArgs { command: Command::ShowVersion, flags: oof::Flags::default() });
}

let subcommands = &[
"c", "compress"
];
let subcommands = &["c", "compress"];

let mut flags_info = vec![flag!('y', "yes"), flag!('n', "no")];

Expand All @@ -115,41 +103,33 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {

let files = canonicalize_files(files)?;

let command = Command::Compress {
files,
compressed_output_path,
};
let command = Command::Compress { files, compressed_output_path };
ParsedArgs { command, flags }
}
},
// Defaults to decompression when there is no subcommand
None => {
flags_info.push(arg_flag!('o', "output"));
{
let first_arg = args.first().unwrap();

if let Some(first_arg) = args.first() {
if is_typo(first_arg) {
return Err(crate::Error::CompressionTypo);
}
} else {
todo!("Complain that no decompression arguments were given.");
}


// Parse flags
let (args, mut flags) = oof::filter_flags(args, &flags_info)?;

let files = args
.into_iter()
.map(canonicalize)
.collect::<Result<Vec<_>, _>>()?;
let files = args.into_iter().map(canonicalize).collect::<Result<Vec<_>, _>>()?;

let output_folder = flags.take_arg("output").map(PathBuf::from);

// TODO: ensure all files are decompressible

let command = Command::Decompress {
files,
output_folder,
};
let command = Command::Decompress { files, output_folder };
ParsedArgs { command, flags }
}
},
_ => unreachable!("You should match each subcommand passed."),
};

Expand Down
Loading

0 comments on commit afbda44

Please sign in to comment.