Compare commits

...

3 commits

Author SHA1 Message Date
4412161193 nicer output for --help
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-01 20:20:46 +10:00
5907309689 renamed modules in accordance with https://rust-lang.github.io/api-guidelines/naming.html 2021-03-01 19:21:00 +10:00
32da919f81 replace (ScanError, PathBuf) with just ScanError (which now contains a PathBuf) 2021-03-01 19:18:20 +10:00
9 changed files with 53 additions and 34 deletions

View file

@ -14,6 +14,9 @@ exclude = [".idea/", "Cross.toml", "*.sh", "*.py", ".drone.yml"]
#resolver = "2" #resolver = "2"
#license-file = "LICENSE" #license-file = "LICENSE"
[badges]
maintenance = { status = "experimental" }
[features] [features]
default = ["multi-threaded"] default = ["multi-threaded"]
multi-threaded = ["rayon"] multi-threaded = ["rayon"]

View file

@ -7,14 +7,14 @@ use std::path::PathBuf;
use snailquote::escape; use snailquote::escape;
use crate::scanerror::ScanError; use crate::scan_error::ScanError;
use crate::{Findings, BACKEND}; use crate::{Findings, BACKEND};
/// The current version of fif, as defined in Cargo.toml. /// The current version of fif, as defined in Cargo.toml.
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
#[doc(hidden)] #[doc(hidden)]
type Entries = [Result<Findings, (ScanError, PathBuf)>]; type Entries = [Result<Findings, ScanError>];
enum Writable<'a> { enum Writable<'a> {
String(&'a str), String(&'a str),
@ -86,11 +86,11 @@ pub trait Format {
Err(error) => { Err(error) => {
// something went wrong 0uo // something went wrong 0uo
match error.0 { match error {
// failed to read the file // failed to read the file
ScanError::File => self.unreadable(f, &error.1)?, ScanError::File(path) => self.unreadable(f, path)?,
// file was read successfully, but we couldn't determine a mimetype // file was read successfully, but we couldn't determine a mimetype
ScanError::Mime => self.unknown_type(f, &error.1)?, ScanError::Mime(path) => self.unknown_type(f, path)?,
} }
} }
} }

View file

@ -10,7 +10,7 @@ use cached::cached;
use mime_guess::Mime; use mime_guess::Mime;
use smartstring::alias::String; use smartstring::alias::String;
use crate::mimedb::MimeDb; use crate::mime_db::MimeDb;
/// The number of bytes to read initially. /// The number of bytes to read initially.
/// ///

View file

@ -28,32 +28,32 @@ use walkdir::{DirEntry, WalkDir};
use crate::findings::Findings; use crate::findings::Findings;
use crate::formats::{Format, Script}; use crate::formats::{Format, Script};
use crate::mimedb::MimeDb; use crate::mime_db::MimeDb;
use crate::parameters::OutputFormat; use crate::parameters::OutputFormat;
use crate::scanerror::ScanError; use crate::scan_error::ScanError;
use env_logger::Env; use env_logger::Env;
use std::process::exit; use std::process::exit;
mod extensionset; mod extension_set;
mod findings; mod findings;
mod formats; mod formats;
mod inspectors; mod inspectors;
mod mimedb; mod mime_db;
mod parameters; mod parameters;
mod scanerror; mod scan_error;
#[cfg(test)] #[cfg(test)]
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(not(unix), not(feature = "xdg-mime-backend")), all(unix, feature = "infer-backend")))] {
/// A [OnceCell] holding an instance of [mimedb::MimeDb]. /// A [OnceCell] holding an instance of [mime_db::MimeDb].
static MIMEDB: OnceCell<mimedb::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".
const BACKEND: &str = "Infer"; const BACKEND: &str = "Infer";
} else { } else {
/// A [OnceCell] holding an instance of [mimedb::MimeDb]. /// A [OnceCell] holding an instance of [mime_db::MimeDb].
static MIMEDB: OnceCell<mimedb::XdgDb> = OnceCell::new(); static MIMEDB: OnceCell<mime_db::XdgDb> = OnceCell::new();
/// The backend being used; either "Infer" or "XDG-Mime". /// The backend being used; either "Infer" or "XDG-Mime".
const BACKEND: &str = "XDG-Mime"; const BACKEND: &str = "XDG-Mime";
} }
@ -116,7 +116,7 @@ fn main() {
r.recommended_extension().unwrap_or_else(|| "???".into()) r.recommended_extension().unwrap_or_else(|| "???".into())
) )
} }
Err(f) => warn!("{:#?}: Error 0uo - {}", f.1, f.0), Err(f) => warn!("Error 0uo - {}", f),
} }
} }
@ -191,26 +191,26 @@ fn extension_from_path(path: &Path) -> Option<String> {
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
} }
/// Inspects the given entry, returning a [Findings] on success and a tuple of [ScanError] and [PathBuf] on failure. /// Inspects the given entry, returning a [Findings] on success and a [ScanError] on failure.
/// ///
/// In the event of an IO error, the returned ScanError will be of type [ScanError::File]. Otherwise, a /// In the event of an IO error, the returned ScanError will be of type [ScanError::File]. Otherwise, a
/// [ScanError::Mime] will be returned, meaning that the file was scanned successfully, but a mimetype could not be /// [ScanError::Mime] will be returned, meaning that the file was scanned successfully, but a mimetype could not be
/// determined. /// determined.
fn scan_file(entry: &DirEntry) -> Result<Findings, (ScanError, PathBuf)> { fn scan_file(entry: &DirEntry) -> Result<Findings, ScanError> {
// try to determine mimetype for this entry // try to determine mimetype for this entry
let result = inspectors::mime_type(MIMEDB.get().unwrap(), entry.path()); let result = inspectors::mime_type(MIMEDB.get().unwrap(), entry.path());
if result.is_err() { if result.is_err() {
// an error occurred while trying to read the file // an error occurred while trying to read the file
// error!("{}: {}", entry.path().to_string_lossy(), error); // error!("{}: {}", entry.path().to_string_lossy(), error);
return Err((ScanError::File, entry.path().to_path_buf())); return Err(ScanError::File(entry.path().to_path_buf()));
} }
let result = result.unwrap(); let result = result.unwrap();
if result.is_none() { if result.is_none() {
// the file was read successfully, but we were unable to determine its mimetype // the file was read successfully, but we were unable to determine its mimetype
// warn!("Couldn't determine mimetype for {}", entry.path().to_string_lossy()); // warn!("Couldn't determine mimetype for {}", entry.path().to_string_lossy());
return Err((ScanError::Mime, entry.path().to_path_buf())); return Err(ScanError::Mime(entry.path().to_path_buf()));
} }
let result = result.unwrap(); let result = result.unwrap();
@ -235,7 +235,7 @@ fn scan_file(entry: &DirEntry) -> Result<Findings, (ScanError, PathBuf)> {
} }
/// Takes a slice of [DirEntry]s and calls [scan_file] on each one, returning the results in a vector. /// Takes a slice of [DirEntry]s and calls [scan_file] on each one, returning the results in a vector.
fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, (ScanError, PathBuf)>> { fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
cfg_if! { cfg_if! {
if #[cfg(feature = "multi-threaded")] { if #[cfg(feature = "multi-threaded")] {
// rather than using a standard par_iter, split the entries into chunks of 32 first. // rather than using a standard par_iter, split the entries into chunks of 32 first.
@ -301,12 +301,12 @@ 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(not(unix), not(feature = "xdg-mime-backend")), all(unix, feature = "infer-backend")))] {
MIMEDB MIMEDB
.set(mimedb::InferDb::init()) .set(mime_db::InferDb::init())
.or(Err("Failed to initialise Infer backend!")) .or(Err("Failed to initialise Infer backend!"))
.unwrap(); .unwrap();
} else { } else {
MIMEDB MIMEDB
.set(mimedb::XdgDb::init()) .set(mime_db::XdgDb::init())
.or(Err("Failed to initialise XDG Mime backend!")) .or(Err("Failed to initialise XDG Mime backend!"))
.unwrap(); .unwrap();
} }

View file

@ -2,20 +2,32 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::extensionset::ExtensionSet; use crate::extension_set::ExtensionSet;
use clap::{Clap}; use clap::{Clap, AppSettings};
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
#[derive(Clap, PartialEq, Debug)] #[derive(Clap, PartialEq, Debug)]
pub enum OutputFormat { pub enum OutputFormat {
/// A Bourne shell compatible script.
Script, Script,
/// Plain text.
Text, Text,
} }
// TODO: convert this to macro style?: https://docs.rs/clap/3.0.0-beta.2/clap/index.html#using-macros // TODO: convert this to macro style?: https://docs.rs/clap/3.0.0-beta.2/clap/index.html#using-macros
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
#[clap(version = option_env!("CARGO_PKG_VERSION").unwrap_or("???"))] #[clap(
version = option_env!("CARGO_PKG_VERSION").unwrap_or("???"),
author = option_env!("CARGO_PKG_AUTHORS").unwrap_or("Lynnesbian"),
about = option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("File Info Fixer"),
before_help = "Copyright © 2021 Lynnesbian under the GPL3 (or later) License.",
before_long_help = "Copyright © 2021 Lynnesbian\n\
This program is free software: you can redistribute it and/or modify \
it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 \
of the License, or (at your option) any later version.",
setting(AppSettings::ColoredHelp)
)]
pub struct Parameters { pub struct Parameters {
/// Only examine files with these extensions (Comma-separated list) /// Only examine files with these extensions (Comma-separated list)
#[clap( #[clap(
@ -27,7 +39,7 @@ pub struct Parameters {
)] )]
pub exts: Option<Vec<SmartString<LazyCompact>>>, pub exts: Option<Vec<SmartString<LazyCompact>>>,
/// write good docs 0uo /// Use a preset list of extensions as the search filter
#[clap(short = 'E', long, arg_enum, required_unless_present = "exts")] #[clap(short = 'E', long, arg_enum, required_unless_present = "exts")]
pub ext_set: Option<ExtensionSet>, pub ext_set: Option<ExtensionSet>,
@ -35,7 +47,7 @@ pub struct Parameters {
#[clap(short, long)] #[clap(short, long)]
pub scan_hidden: bool, pub scan_hidden: bool,
/// Output format to use. See "--help formats" for more information. /// 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,

View file

@ -1,21 +1,25 @@
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug)]
pub enum ScanError { pub enum ScanError {
/// Something went wrong while trying to read the given file. /// Something went wrong while trying to read the given file.
File, File(PathBuf),
/// Failed to determine the mimetype of the given file. /// Failed to determine the mimetype of the given file.
Mime, Mime(PathBuf),
} }
impl Display for ScanError { impl Display for ScanError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result { fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!( write!(
f, f,
"{}", "Couldn't {} file: {}",
match self { match self {
Self::File => "Couldn't read file", Self::File(_) => "read",
Self::Mime => "Couldn't determine mime type", Self::Mime(_) => "determine mime type of",
},
match self {
Self::File(f) | Self::Mime(f) => f.to_string_lossy(),
} }
) )
} }

View file

@ -1,5 +1,5 @@
use crate::inspectors::mime_extension_lookup; use crate::inspectors::mime_extension_lookup;
use crate::mimedb::*; 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;