new option for scanning extensionless files, nicer #[cfg]s, -s no longer skips hidden root dir...
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing

This commit is contained in:
Lynne Megido 2021-03-03 01:12:29 +10:00
parent 4412161193
commit 8a152d4118
Signed by: lynnesbian
GPG key ID: F0A184B5213D9F90
6 changed files with 47 additions and 18 deletions

2
Cargo.lock generated
View file

@ -168,7 +168,7 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
[[package]] [[package]]
name = "fif" name = "fif"
version = "0.2.7" version = "0.2.8"
dependencies = [ dependencies = [
"cached", "cached",
"cfg-if", "cfg-if",

View file

@ -1,7 +1,7 @@
[package] [package]
name = "fif" name = "fif"
description = "A command-line tool for detecting and optionally correcting files with incorrect extensions." description = "A command-line tool for detecting and optionally correcting files with incorrect extensions."
version = "0.2.7" version = "0.2.8"
authors = ["Lynnesbian <lynne@bune.city>"] authors = ["Lynnesbian <lynne@bune.city>"]
edition = "2018" edition = "2018"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
@ -35,7 +35,7 @@ rayon = { version = "1.5.0", optional = true }
exitcode = "1.1.2" exitcode = "1.1.2"
cfg-if = "1.0.0" cfg-if = "1.0.0"
[target.'cfg(any(unix, target_os="redox"))'.dependencies] [target.'cfg(unix)'.dependencies]
xdg-mime = "0.3" xdg-mime = "0.3"
[patch.crates-io] [patch.crates-io]

View file

@ -29,7 +29,7 @@ use walkdir::{DirEntry, WalkDir};
use crate::findings::Findings; use crate::findings::Findings;
use crate::formats::{Format, Script}; use crate::formats::{Format, Script};
use crate::mime_db::MimeDb; use crate::mime_db::MimeDb;
use crate::parameters::OutputFormat; use crate::parameters::{OutputFormat, ScanOpts};
use crate::scan_error::ScanError; use crate::scan_error::ScanError;
use env_logger::Env; use env_logger::Env;
use std::process::exit; use std::process::exit;
@ -46,7 +46,7 @@ mod scan_error;
mod tests; mod tests;
cfg_if! { cfg_if! {
if #[cfg(any(all(not(unix), not(feature = "xdg-mime-backend")), all(unix, feature = "infer-backend")))] { if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
/// A [OnceCell] holding an instance of [mime_db::MimeDb]. /// A [OnceCell] holding an instance of [mime_db::MimeDb].
static MIMEDB: OnceCell<mime_db::InferDb> = OnceCell::new(); static MIMEDB: OnceCell<mime_db::InferDb> = OnceCell::new();
/// The backend being used; either "Infer" or "XDG-Mime". /// The backend being used; either "Infer" or "XDG-Mime".
@ -80,7 +80,7 @@ fn main() {
debug!("Checking files with extensions: {:?}", extensions); debug!("Checking files with extensions: {:?}", extensions);
let entries = scan_directory(&args.dirs, &extensions, args.scan_hidden); let entries = scan_directory(&args.dirs, &extensions, &args.get_scan_opts());
if entries.is_none() { if entries.is_none() {
// no need to log anything for fatal errors - fif will already have printed something obvious like // no need to log anything for fatal errors - fif will already have printed something obvious like
@ -161,9 +161,15 @@ cfg_if! {
} }
} }
/// Returns `true` if a file matches the given criteria. /// Returns `true` if a file matches the given criteria. This means checking whether the file's extension appears in
fn wanted_file(scan_hidden: bool, exts: &[&str], entry: &DirEntry) -> bool { /// `exts`, potentially skipping over hidden files, and so on.
if !scan_hidden && is_hidden(entry) { fn wanted_file(entry: &DirEntry, exts: &[&str], scan_opts: &ScanOpts) -> bool {
if entry.depth() == 0 {
// the root directory should always be scanned.
return true;
}
if !scan_opts.hidden && is_hidden(entry) {
// skip hidden files and directories. this check is performed first because it's very lightweight. // skip hidden files and directories. this check is performed first because it's very lightweight.
return false; return false;
} }
@ -175,9 +181,10 @@ fn wanted_file(scan_hidden: bool, exts: &[&str], entry: &DirEntry) -> bool {
let ext = extension_from_path(entry.path()); let ext = extension_from_path(entry.path());
if ext.is_none() { if ext.is_none() && !scan_opts.extensionless {
// don't scan files without extensions.
return false; return false;
} // don't scan files without extensions. TODO - this should be configurable }
exts.contains(&ext.unwrap().to_lowercase().as_str()) exts.contains(&ext.unwrap().to_lowercase().as_str())
} }
@ -186,6 +193,7 @@ fn wanted_file(scan_hidden: bool, exts: &[&str], entry: &DirEntry) -> bool {
/// ///
/// The extension is currently [converted to a lossy string](std::ffi::OsStr::to_string_lossy), although it will /// The extension is currently [converted to a lossy string](std::ffi::OsStr::to_string_lossy), although it will
/// (eventually) in future return an OsStr instead. /// (eventually) in future return an OsStr instead.
// TODO: ↑
fn extension_from_path(path: &Path) -> Option<String> { fn extension_from_path(path: &Path) -> Option<String> {
path.extension(). // Get the path's extension path.extension(). // Get the path's extension
map(|e| String::from(e.to_string_lossy())) // Convert from OsStr to String map(|e| String::from(e.to_string_lossy())) // Convert from OsStr to String
@ -258,11 +266,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 /// Scans a given directory with [WalkDir], filters with [wanted_file], checks for errors, and returns a vector of
/// [DirEntry]s. /// [DirEntry]s.
fn scan_directory(dirs: &PathBuf, exts: &Vec<&str>, scan_hidden: bool) -> Option<Vec<DirEntry>> { fn scan_directory(dirs: &PathBuf, exts: &Vec<&str>, scan_opts: &ScanOpts) -> Option<Vec<DirEntry>> {
let stepper = WalkDir::new(dirs).into_iter(); let stepper = WalkDir::new(dirs).into_iter();
let mut probably_fatal_error = false; let mut probably_fatal_error = false;
let entries: Vec<DirEntry> = stepper let entries: Vec<DirEntry> = stepper
.filter_entry(|e| wanted_file(scan_hidden, exts, e)) // filter out unwanted files .filter_entry(|e| wanted_file(e, exts, scan_opts)) // filter out unwanted files
.filter_map(|e| { .filter_map(|e| {
if let Err(err) = &e { if let Err(err) = &e {
debug!("uh oh spaghettio!! {:#?}", e); debug!("uh oh spaghettio!! {:#?}", e);
@ -299,7 +307,7 @@ fn scan_directory(dirs: &PathBuf, exts: &Vec<&str>, scan_hidden: bool) -> Option
/// Initialises [MIMEDB] with a value dependent on the current backend. /// Initialises [MIMEDB] with a value dependent on the current backend.
fn init_db() { fn init_db() {
cfg_if! { cfg_if! {
if #[cfg(any(all(not(unix), not(feature = "xdg-mime-backend")), all(unix, feature = "infer-backend")))] { if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
MIMEDB MIMEDB
.set(mime_db::InferDb::init()) .set(mime_db::InferDb::init())
.or(Err("Failed to initialise Infer backend!")) .or(Err("Failed to initialise Infer backend!"))

View file

@ -9,7 +9,7 @@ pub trait MimeDb {
} }
cfg_if! { cfg_if! {
if #[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))] { if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
use std::str::FromStr; use std::str::FromStr;
pub struct InferDb { pub struct InferDb {

View file

@ -47,6 +47,10 @@ pub struct Parameters {
#[clap(short, long)] #[clap(short, long)]
pub scan_hidden: bool, pub scan_hidden: bool,
/// Scan files without extensions
#[clap(short = 'S', long)]
pub scan_extensionless: bool,
/// Output format to use /// Output format to use
#[clap(short, long, default_value = "script", arg_enum)] #[clap(short, long, default_value = "script", arg_enum)]
pub output_format: OutputFormat, pub output_format: OutputFormat,
@ -57,6 +61,14 @@ pub struct Parameters {
pub dirs: PathBuf, pub dirs: PathBuf,
} }
/// Further options relating to scanning.
pub struct ScanOpts {
/// Whether hidden files and directories should be scanned.
pub hidden: bool,
/// Whether files without extensions should be scanned.
pub extensionless: bool
}
impl Parameters { impl Parameters {
pub fn extensions(&self) -> Vec<&str> { pub fn extensions(&self) -> Vec<&str> {
if let Some(exts) = &self.exts { if let Some(exts) = &self.exts {
@ -70,4 +82,8 @@ impl Parameters {
unreachable!() unreachable!()
} }
} }
pub fn get_scan_opts(&self) -> ScanOpts {
ScanOpts { hidden: self.scan_hidden, extensionless: self.scan_extensionless }
}
} }

View file

@ -2,7 +2,7 @@ use crate::inspectors::mime_extension_lookup;
use crate::mime_db::*; use crate::mime_db::*;
use crate::{extension_from_path, init_db, scan_directory, scan_from_walkdir}; use crate::{extension_from_path, init_db, scan_directory, scan_from_walkdir};
use crate::parameters::Parameters; use crate::parameters::{Parameters, ScanOpts};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use mime_guess::mime::{APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG}; use mime_guess::mime::{APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG};
use mime_guess::Mime; use mime_guess::Mime;
@ -16,7 +16,7 @@ const PDF_BYTES: &[u8] = b"%PDF-";
const ZIP_BYTES: &[u8] = b"PK\x03\x04"; const ZIP_BYTES: &[u8] = b"PK\x03\x04";
cfg_if! { cfg_if! {
if #[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))] { if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
fn get_mime_db() -> InferDb { fn get_mime_db() -> InferDb {
InferDb::init() InferDb::init()
} }
@ -95,10 +95,15 @@ fn simple_directory() {
drop(file); drop(file);
} }
let scan_opts = ScanOpts{
hidden: true,
extensionless: false
};
let entries = scan_directory( let entries = scan_directory(
&dir.path().to_path_buf(), &dir.path().to_path_buf(),
&vec!["jpg", "jpeg", "png", "pdf", "zip"], &vec!["jpg", "jpeg", "png", "pdf", "zip"],
true, &scan_opts,
) )
.expect("Directory scan failed."); .expect("Directory scan failed.");