Compare commits
No commits in common. "7e3efbed5c93dbcb58d20ae606712627680a2109" and "af9968947c75bc0a50f1744416314bce2b5295c5" have entirely different histories.
7e3efbed5c
...
af9968947c
7 changed files with 28 additions and 108 deletions
|
@ -2,10 +2,6 @@
|
|||
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`)
|
||||
|
||||
### v0.2.13 (2021-04-26)
|
||||
#### Features
|
||||
- Added `-v`/`--verbose` flag for setting verbosity without using `RUST_LOG`
|
||||
|
|
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -313,9 +313,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.94"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
|
|
@ -10,7 +10,7 @@ repository = "https://gitlab.com/Lynnesbian/fif"
|
|||
readme = "README.md"
|
||||
keywords = ["mime", "mimetype", "utilities", "tools"]
|
||||
categories = ["command-line-utilities"]
|
||||
exclude = [".idea/", "*.toml", "!Cargo.toml", "*.sh", "*.py", "*.yml", "*.md", ".mailmap", "pkg/"]
|
||||
exclude = [".idea/", "Cross.toml", "*.sh", "*.py", ".drone.yml", "pkg/"]
|
||||
#resolver = "2"
|
||||
#license-file = "LICENSE"
|
||||
|
||||
|
@ -66,9 +66,8 @@ fastrand = "1.4.1"
|
|||
[profile.release]
|
||||
lto = "thin"
|
||||
|
||||
# perform some simple optimisations when testing
|
||||
[profile.test]
|
||||
opt-level = 1
|
||||
opt-level = 0
|
||||
|
||||
# optimise dependencies, even when producing debug and test builds
|
||||
[profile.dev.package."*"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
fd -e rs -x touch {}
|
||||
cargo clippy --all-features --tests -- \
|
||||
cargo clippy --all-features -- \
|
||||
-W clippy::nursery \
|
||||
-W clippy::perf \
|
||||
-W clippy::pedantic \
|
||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -78,17 +78,10 @@ fn main() {
|
|||
debug!("Iterating directory: {:?}", args.dirs);
|
||||
|
||||
let extensions = args.extensions();
|
||||
let excludes = args.excluded_extensions();
|
||||
|
||||
if let Some(extensions) = &extensions {
|
||||
debug!("Checking files with extensions: {:?}", extensions);
|
||||
} else if let Some(excludes) = &excludes {
|
||||
debug!("Skipping files with extensions: {:?}", excludes);
|
||||
} else {
|
||||
debug!("Checking files regardless of extensions");
|
||||
}
|
||||
|
||||
let entries = scan_directory(&args.dirs, extensions.as_ref(), excludes.as_ref(), &args.get_scan_opts());
|
||||
let entries = scan_directory(&args.dirs, extensions.as_ref(), &args.get_scan_opts());
|
||||
|
||||
if entries.is_none() {
|
||||
// no need to log anything for fatal errors - fif will already have printed something obvious like
|
||||
|
@ -179,7 +172,7 @@ cfg_if! {
|
|||
|
||||
/// Returns `true` if a file matches the given criteria. This means checking whether the file's extension appears in
|
||||
/// `exts` (if specified), potentially skipping over hidden files, and so on.
|
||||
fn wanted_file(entry: &DirEntry, exts: Option<&Vec<&str>>, exclude: Option<&Vec<&str>>, scan_opts: &ScanOpts) -> bool {
|
||||
fn wanted_file(entry: &DirEntry, exts: Option<&Vec<&str>>, scan_opts: &ScanOpts) -> bool {
|
||||
if entry.depth() == 0 {
|
||||
// the root directory should always be scanned.
|
||||
return true;
|
||||
|
@ -195,21 +188,19 @@ fn wanted_file(entry: &DirEntry, exts: Option<&Vec<&str>>, exclude: Option<&Vec<
|
|||
return true;
|
||||
}
|
||||
|
||||
if let Some(ext) = extension_from_path(entry.path()) {
|
||||
// file has extension - discard invalid UTF-8 and normalise it to lowercase.
|
||||
let ext = ext.to_string_lossy().to_lowercase();
|
||||
let ext = ext.as_str();
|
||||
let ext = extension_from_path(entry.path());
|
||||
|
||||
if ext.is_none() && !scan_opts.extensionless {
|
||||
// don't scan files without extensions.
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(exts) = exts {
|
||||
// only scan if the file has one of the specified extensions.
|
||||
exts.contains(&ext)
|
||||
exts.contains(&ext.unwrap().to_string_lossy().to_lowercase().as_str())
|
||||
} else {
|
||||
// no extensions specified - the file should be scanned unless its extension is on the exclude list.
|
||||
exclude.map_or(true, |exclude| !exclude.contains(&ext))
|
||||
}
|
||||
} else {
|
||||
// no file extension
|
||||
scan_opts.extensionless
|
||||
// no extensions specified - no reason not to scan this file.
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,11 +276,11 @@ fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
|
|||
|
||||
/// Scans a given directory with [`WalkDir`], filters with [`wanted_file`], checks for errors, and returns a vector of
|
||||
/// [DirEntry]s.
|
||||
fn scan_directory(dirs: &Path, exts: Option<&Vec<&str>>, exclude: Option<&Vec<&str>>, scan_opts: &ScanOpts) -> Option<Vec<DirEntry>> {
|
||||
fn scan_directory(dirs: &Path, exts: Option<&Vec<&str>>, scan_opts: &ScanOpts) -> Option<Vec<DirEntry>> {
|
||||
let stepper = WalkDir::new(dirs).follow_links(scan_opts.follow_symlinks).into_iter();
|
||||
let mut probably_fatal_error = false;
|
||||
let entries: Vec<DirEntry> = stepper
|
||||
.filter_entry(|e| wanted_file(e, exts, exclude, scan_opts)) // filter out unwanted files
|
||||
.filter_entry(|e| wanted_file(e, exts, scan_opts)) // filter out unwanted files
|
||||
.filter_map(|e| {
|
||||
if let Err(err) = &e {
|
||||
debug!("uh oh spaghettio!! {:#?}", e);
|
||||
|
|
|
@ -40,7 +40,7 @@ pub enum OutputFormat {
|
|||
setting(AppSettings::ColoredHelp)
|
||||
)]
|
||||
pub struct Parameters {
|
||||
/// Only examine files with these extensions (comma-separated list).
|
||||
/// Only examine files with these extensions (Comma-separated list).
|
||||
/// This argument conflicts with `-E`.
|
||||
#[clap(short, long, use_delimiter = true, require_delimiter = true, group = "extensions")]
|
||||
pub exts: Option<Vec<StringType>>,
|
||||
|
@ -50,16 +50,6 @@ pub struct Parameters {
|
|||
#[clap(short = 'E', long, arg_enum, group = "extensions")]
|
||||
pub ext_set: Option<ExtensionSet>,
|
||||
|
||||
/// Don't scan files with these extensions (comma-separated list).
|
||||
/// This option takes preference over files specified with -e or -E.
|
||||
#[clap(
|
||||
short = 'x',
|
||||
long,
|
||||
use_delimiter = true,
|
||||
require_delimiter = true,
|
||||
)]
|
||||
pub exclude: Option<Vec<StringType>>,
|
||||
|
||||
/// 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
|
||||
/// ~/.hidden` will recurse into `~/.hidden` regardless of whether or not -s was passed as an argument.
|
||||
|
@ -104,41 +94,19 @@ pub struct ScanOpts {
|
|||
}
|
||||
|
||||
impl Parameters {
|
||||
/// Returns an optional vec of the extensions to be scanned - i.e., extensions specified via the `-e` or `-E` flag,
|
||||
/// minus the extensions excluded with the `-x` flag.
|
||||
pub fn extensions(&self) -> Option<Vec<&str>> {
|
||||
let empty_vec = vec![];
|
||||
let exclude = &self.excluded_extensions().unwrap_or(empty_vec);
|
||||
|
||||
// TODO: bleugh
|
||||
if let Some(exts) = &self.exts {
|
||||
// extensions supplied like "-e png,jpg,jpeg"
|
||||
Some(
|
||||
exts
|
||||
.iter()
|
||||
.map(|ext| ext.as_str())
|
||||
.filter(|ext| !exclude.contains(ext))
|
||||
.collect(),
|
||||
)
|
||||
Some(exts.iter().map(|s| s.as_str()).collect())
|
||||
} else if let Some(exts) = &self.ext_set {
|
||||
// extensions supplied like "-E images"
|
||||
Some(
|
||||
exts
|
||||
.extensions()
|
||||
.into_iter()
|
||||
.filter(|ext| !exclude.contains(ext))
|
||||
.collect(),
|
||||
)
|
||||
Some(exts.extensions())
|
||||
} else {
|
||||
// neither -E nor -e was passed
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn excluded_extensions(&self) -> Option<Vec<&str>> {
|
||||
self.exclude.as_ref().map(|exclude| exclude.iter().map(|ext| ext.as_str()).collect())
|
||||
}
|
||||
|
||||
pub const fn get_scan_opts(&self) -> ScanOpts {
|
||||
ScanOpts {
|
||||
hidden: self.scan_hidden,
|
||||
|
|
|
@ -113,7 +113,7 @@ fn simple_directory() {
|
|||
follow_symlinks: false,
|
||||
};
|
||||
|
||||
let entries = scan_directory(&dir.path().to_path_buf(), None, None, &scan_opts).expect("Directory scan failed.");
|
||||
let entries = scan_directory(&dir.path().to_path_buf(), None, &scan_opts).expect("Directory scan failed.");
|
||||
|
||||
assert_eq!(entries.len(), files.len());
|
||||
|
||||
|
@ -168,6 +168,7 @@ fn simple_directory() {
|
|||
/// 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;
|
||||
|
||||
// pass `-f`, which enables following symlinks, and `-E images`, which scans files with image extensions
|
||||
|
@ -176,10 +177,8 @@ fn argument_parsing() {
|
|||
// check if "jpg" is in the list of extensions to be scanned
|
||||
assert!(args
|
||||
.extensions()
|
||||
.expect("args.extensions() should be Some(_)!")
|
||||
.contains(&"jpg"),
|
||||
"args.extensions() should contain the `images` set!"
|
||||
);
|
||||
.expect("args.extensions() should contain the `images` set!")
|
||||
.contains(&"jpg"));
|
||||
|
||||
// make sure "scan_hidden" is false
|
||||
assert!(!args.scan_hidden);
|
||||
|
@ -187,9 +186,6 @@ fn argument_parsing() {
|
|||
// exts should be none
|
||||
assert!(args.exts.is_none());
|
||||
|
||||
// there shouldn't be any excluded extensions
|
||||
assert!(args.excluded_extensions().is_none());
|
||||
|
||||
// get the ScanOpts, and make sure they match expectations
|
||||
assert_eq!(
|
||||
args.get_scan_opts(),
|
||||
|
@ -202,36 +198,6 @@ fn argument_parsing() {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Ensure exclude 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"]);
|
||||
let extensions = args.extensions();
|
||||
assert!(extensions.is_some(), "Extensions should contain the `images` set!");
|
||||
let extensions = extensions.unwrap();
|
||||
|
||||
assert!(!extensions.contains(&"jpg"), "\"jpg\" should be excluded!");
|
||||
assert!(!extensions.contains(&"png"), "\"png\" should be excluded!");
|
||||
assert!(extensions.contains(&"jpeg"), "\"jpeg\" should be included!");
|
||||
|
||||
// pass `-e abc,def,ghi,jkl` and `-x abc,def` -- extensions() should only contain "ghi" and "jkl"
|
||||
let args: Parameters = Parameters::parse_from(vec!["fif", "-e", "abc,def,ghi,jkl", "-x", "abc,def"]);
|
||||
let extensions = args.extensions();
|
||||
assert!(extensions.is_some(), "Extensions should be set!");
|
||||
let extensions = extensions.unwrap();
|
||||
|
||||
assert!(!extensions.contains(&"abc"));
|
||||
assert!(!extensions.contains(&"def"));
|
||||
assert!(extensions.contains(&"ghi"));
|
||||
assert!(extensions.contains(&"jkl"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
/// Ensure that badly formed command line arguments are rejected.
|
||||
fn rejects_bad_args() {
|
||||
|
|
Loading…
Reference in a new issue