diff --git a/CHANGELOG.md b/CHANGELOG.md index e5845f3..5e83812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,11 @@ Dates are given in YYYY-MM-DD format. ## v0.2 ### v0.2.14 (2021-xx-yy) #### Features -- Added `-x`/`--exclude` flag for excluding file extensions (overrides `-e` or `-E`) - +- Added `-x`/`--exclude` flag for excluding file extensions (overrides `-e` or `-E` - `-E images -x jpg` scans all image + files, except ".jpg" files) +- Added `-X`/`--exclude-set` flag for excluding sets of files (like `-E`) #### Other -- Publish my fork of ['mime_guess'] as ['new_mime_guess'], allowing it to be used properly with +- Published my fork of ['mime_guess'] as ['new_mime_guess'], allowing it to be used properly with [crates.io](https://crates.io) ### v0.2.13 (2021-04-26) @@ -152,5 +153,5 @@ Initial commit! [`clap`]: https://crates.io/crates/clap [`infer`]: https://crates.io/crates/infer [`mime_guess`]: https://crates.io/crates/mime_guess -[`new_mime_guess`]: https://crates.io/crates/mime_guess -[`snailquote`]: https://crates.io/crates/snailquote \ No newline at end of file +[`new_mime_guess`]: https://crates.io/crates/new_mime_guess +[`snailquote`]: https://crates.io/crates/snailquote diff --git a/src/parameters.rs b/src/parameters.rs index edc639a..02fdfea 100644 --- a/src/parameters.rs +++ b/src/parameters.rs @@ -55,7 +55,9 @@ pub struct Parameters { #[clap(short = 'x', long, use_delimiter = true, require_delimiter = true)] pub exclude: Option>, - // TODO: -X/--exclude-set - allows you to exclude all extensions in a set, e.g. "-X documents" + /// Exclude files using a preset list of extensions. + #[clap(short = 'X', long, arg_enum)] + pub exclude_set: Option, /// Don't skip hidden files and directories. /// Even if this flag is not present, fif will still recurse into a hidden root directory - for example, `fif @@ -133,10 +135,26 @@ impl Parameters { } pub fn excluded_extensions(&self) -> Option> { - self - .exclude - .as_ref() - .map(|exclude| exclude.iter().map(|ext| ext.as_str()).collect()) + // start with an empty vec + let mut excluded = vec![]; + if let Some(exclude) = self.exclude.as_ref() { + // add extensions excluded by `-x` + excluded.append(&mut exclude.iter().map(|ext| ext.as_str()).collect()); + } + + if let Some(exclude_set) = &self.exclude_set { + // add extensions excluded by `-X` + excluded.append(&mut exclude_set.extensions()); + } + + if excluded.is_empty() { + // no extensions to exclude - return none + None + } else { + // excluded doesn't sound like a word anymore + // tongue twister: enter X-options' excellent extension exclusion + Some(excluded) + } } pub const fn get_scan_opts(&self) -> ScanOpts { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index f831d9d..b9b0d28 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -7,10 +7,13 @@ use crate::{extension_from_path, scan_directory, scan_from_walkdir}; use mime_guess::mime::{APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG}; use mime_guess::Mime; +use crate::parameters::Parameters; +use clap::Clap; use std::collections::HashMap; use std::ffi::OsStr; use std::path::Path; +use crate::parameters::ExtensionSet; const JPEG_BYTES: &[u8] = b"\xFF\xD8\xFF"; const PNG_BYTES: &[u8] = b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; @@ -167,8 +170,7 @@ fn simple_directory() { #[test] /// Ensure that command line argument parsing works correctly - flags are interpreted, booleans are set, and so on. fn argument_parsing() { - use crate::parameters::{Parameters, ScanOpts}; - use clap::Clap; + use crate::parameters::ScanOpts; // pass `-f`, which enables following symlinks, and `-E images`, which scans files with image extensions let args: Parameters = Parameters::parse_from(vec!["fif", "-f", "-E", "images"]); @@ -204,11 +206,8 @@ fn argument_parsing() { } #[test] -/// Ensure exclude overrides `-e` and `-E`. +/// Ensure the `exclude` flag (`-x`) overrides `-e` and `-E`. fn exclude_overrides() { - use crate::parameters::Parameters; - use clap::Clap; - // pass `-E images`, which includes many image extensions, and `-x jpg,png`, which should remove "jpg" and "png" from // the extensions list let args: Parameters = Parameters::parse_from(vec!["fif", "-x", "jpg,png", "-E", "images"]); @@ -232,12 +231,42 @@ fn exclude_overrides() { assert!(extensions.contains(&"jkl")); } +#[test] +/// Ensure the `exclude_set` flag (`-X`) overrides `-e`. +fn exclude_set_overrides_includes() { + // pass `-e jpg,flac` and `-X images` -- which should produce the equivalent of `-e flag` + let args: Parameters = Parameters::parse_from(vec!["fif", "-e", "jpg,flac", "-X", "images"]); + let extensions = args.extensions(); + assert!(extensions.is_some(), "Extensions should be set!"); + let mut extensions = extensions.unwrap().into_iter(); + + assert_eq!(extensions.next(), Some("flac"), "Extensions should contain flac!"); + assert_eq!(extensions.next(), None, "Too many extensions!"); +} + +#[test] +/// Ensure the `exclude_set` flag (`-X`) overrides `-E`. +fn exclude_set_overrides_include_set() { + // pass `-E media` and `-X images` -- which should produce the equivalent of `-E audio,videos` + let args: Parameters = Parameters::parse_from(vec!["fif", "-E", "media", "-X", "images"]); + let extensions = args.extensions(); + assert!(extensions.is_some(), "Extensions should be set!"); + let extensions = extensions.unwrap(); + + // ensure all of audio and video's extensions are here + for &ext in ExtensionSet::Audio.extensions().iter().chain(ExtensionSet::Videos.extensions().iter()) { + assert!(extensions.contains(&ext), "Extensions should contain {}!", ext) + } + + // ensure all of images' extensions are excluded + for ext in ExtensionSet::Images.extensions() { + assert!(!extensions.contains(&ext), "Extensions should not contain {}!", ext) + } +} + #[test] /// Ensure that badly formed command line arguments are rejected. fn rejects_bad_args() { - use crate::parameters::Parameters; - - use clap::Clap; let tests = [ // Non-existent flags: vec!["fif", "-abcdefghijklmnopqrstuvwxyz"],