Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing compression and decompression of formats that support multiple files #24

Merged
merged 7 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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