Compare commits
8 commits
b80c510d48
...
af9968947c
Author | SHA1 | Date | |
---|---|---|---|
af9968947c | |||
b864b4b388 | |||
40bdac274f | |||
889b2e2316 | |||
2ceafb7acf | |||
7a315e9cf6 | |||
475998fd22 | |||
0753c7c948 |
12 changed files with 118 additions and 84 deletions
|
@ -7,7 +7,7 @@ steps:
|
|||
- name: linux-gnu
|
||||
image: rust:latest
|
||||
commands:
|
||||
- cargo test -j3
|
||||
- cargo test --locked -j3
|
||||
#- name: linux-musl
|
||||
# image: rust:alpine
|
||||
# commands:
|
||||
|
|
|
@ -19,12 +19,12 @@ stages:
|
|||
build:
|
||||
stage: build
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo build --verbose --locked
|
||||
|
||||
cargo-test:
|
||||
stage: test
|
||||
script:
|
||||
cargo test --verbose
|
||||
cargo test --verbose --locked
|
||||
|
||||
clippy:
|
||||
stage: test
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
Dates are given in YYYY-MM-DD format.
|
||||
|
||||
## v0.2
|
||||
### v0.2.13 (2021-???)
|
||||
### v0.2.13 (2021-04-26)
|
||||
#### Features
|
||||
- Added `-v`/`--verbose` flag for setting verbosity without using `RUST_LOG`
|
||||
- Added system extension set (`.dll`, `.so`, `.exe`...)
|
||||
- Output is now sorted: Files that couldn't be read, then files with no known mimetype, then files with no known
|
||||
extensions, then files with the wrong extension
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -193,7 +193,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fif"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
dependencies = [
|
||||
"cached",
|
||||
"cfg-if",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "fif"
|
||||
description = "A command-line tool for detecting and optionally correcting files with incorrect extensions."
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
authors = ["Lynnesbian <lynne@bune.city>"]
|
||||
edition = "2018"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
|
45
README.md
45
README.md
|
@ -6,8 +6,10 @@ fif
|
|||
](https://crates.io/crates/fif)
|
||||
[![License](https://img.shields.io/crates/l/fif.svg?style=flat-square)
|
||||
](https://gitlab.com/Lynnesbian/fif/-/blob/master/LICENSE)
|
||||
[![Build status](https://img.shields.io/drone/build/lynnesbian/fif?logo=drone&server=https%3A%2F%2Fdrone.bune.city&style=flat-square)
|
||||
](https://gitlab.com/Lynnesbian/fif/-/blob/master/README.md)
|
||||
[![Drone build status](https://img.shields.io/drone/build/lynnesbian/fif?logo=drone&server=https%3A%2F%2Fdrone.bune.city&style=flat-square)
|
||||
](https://drone.bune.city/lynnesbian/fif)
|
||||
[![GitLab build status](https://img.shields.io/gitlab/pipeline/Lynnesbian/fif/master?logo=gitlab&style=flat-square)
|
||||
](https://gitlab.com/Lynnesbian/fif/-/pipelines/latest)
|
||||
[![Unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)
|
||||
](https://github.com/rust-secure-code/safety-dance/)
|
||||
|
||||
|
@ -60,16 +62,6 @@ cargo install fif --no-default-features
|
|||
## Usage
|
||||
See `fif --help` for more.
|
||||
|
||||
### Logging
|
||||
By default, fif will log any warnings and/or errors encountered during execution. The verbosity of the logging can be
|
||||
modified by the `RUST_LOG` to one of: `trace`, `debug`, `info`, `warn`, `error`.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug fif ~/Downloads
|
||||
```
|
||||
|
||||
### The basics
|
||||
The simplest way to use fif looks like this:
|
||||
|
||||
|
@ -106,3 +98,32 @@ You can also manually specify an output format to use:
|
|||
```bash
|
||||
fif -O powershell ~/Documents > output.ps1
|
||||
```
|
||||
|
||||
### Logging
|
||||
By default, fif will log any warnings and/or errors encountered during execution. This can be changed with the `-v`
|
||||
flag:
|
||||
```bash
|
||||
# also log info
|
||||
fif -v ~/Downloads
|
||||
# ...and debug
|
||||
fif -vv ~/Downloads
|
||||
# ...and trace
|
||||
fif -vvv ~/Downloads
|
||||
```
|
||||
The verbosity of the logging can be
|
||||
modified by the `RUST_LOG` to one of: `trace`, `debug`, `info`, `warn`, `error`.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug fif ~/Downloads
|
||||
```
|
||||
|
||||
The five logging levels are used as follows:
|
||||
| Level | Description | Example |
|
||||
|-|-|-|
|
||||
| error | Errors that cause fif to stop running | fif was unable to open the provided directory |
|
||||
| warn | Warnings that don't cause fif to stop running | fif was unable to determine the mime type of a given file |
|
||||
| info | Information pertaining to fif's status | The provided directory was scanned without issue, and no files are in need of renaming |
|
||||
| debug | Debug information - usually not important to end users | The list of extensions fif will consider |
|
||||
| trace | Trace info - usually not important to end users | "Found 15 items to check", "Scan successful", etc. |
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
fd -e rs -x touch {}
|
||||
cargo clippy --tests -- \
|
||||
cargo clippy --all-features -- \
|
||||
-W clippy::nursery \
|
||||
-W clippy::perf \
|
||||
-W clippy::pedantic \
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
//! Sets of extensions for use with [Parameter](crate::parameters::Parameters)'s `-E` flag.
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
pub enum ExtensionSet {
|
||||
/// Extensions used for image file formats, such as `png`, `jpeg`, `webp`, etc.
|
||||
Images,
|
||||
/// Extensions used for audio file formats, such as `mp3`, `ogg`, `flac`, etc.
|
||||
Audio,
|
||||
/// Extensions used for video file formats, such as `mkv`, `mp4`, `mov`, etc.
|
||||
Videos,
|
||||
/// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images),
|
||||
/// [Audio](ExtensionSet::Audio) and [Videos](ExtensionSet::Videos) variants.
|
||||
Media,
|
||||
/// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc.
|
||||
Documents,
|
||||
/// Extensions used for text file formats, such as `txt`, `toml`, `html`, etc.
|
||||
Text,
|
||||
/// Extensions used for archive file formats, such as `zip`, `zst`, `gz`, etc.
|
||||
Archives,
|
||||
/// Extensions used for system file formats, such as `mbr`, `crash`, `dll`, etc.
|
||||
System,
|
||||
}
|
||||
|
||||
impl ExtensionSet {
|
||||
/// The list of known extensions for this `ExtensionSet`.
|
||||
pub fn extensions(&self) -> Vec<&str> {
|
||||
match self {
|
||||
Self::Images => mime_guess::get_mime_extensions_str("image/*").unwrap().to_vec(),
|
||||
Self::Audio => mime_guess::get_mime_extensions_str("audio/*").unwrap().to_vec(),
|
||||
Self::Videos => mime_guess::get_mime_extensions_str("video/*").unwrap().to_vec(),
|
||||
Self::Media => [
|
||||
Self::Images.extensions(),
|
||||
Self::Audio.extensions(),
|
||||
Self::Videos.extensions(),
|
||||
]
|
||||
.concat(),
|
||||
Self::Documents => vec![
|
||||
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
||||
"pages", "key", "numbers",
|
||||
],
|
||||
Self::Text => mime_guess::get_mime_extensions_str("text/*").unwrap().to_vec(),
|
||||
// many compressed file types follow the name scheme "application/x.+compressed.*" - maybe this can be used
|
||||
// somehow to extract extensions for compressed files from mime_guess?
|
||||
Self::Archives => vec!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz", "rpa"],
|
||||
Self::System => vec![
|
||||
"com", "dll", "exe", "sys", "reg", "nt", "cpl", "msi", "efi", "bio", "rcv", "mbr", "sbf", "grub", "ko",
|
||||
"dylib", "pdb", "hdmp", "crash",
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
13
src/main.rs
13
src/main.rs
|
@ -27,8 +27,6 @@ use clap::Clap;
|
|||
use env_logger::Env;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use once_cell::sync::OnceCell;
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
use rayon::prelude::*;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::findings::Findings;
|
||||
|
@ -37,7 +35,6 @@ use crate::mime_db::MimeDb;
|
|||
use crate::parameters::{OutputFormat, ScanOpts};
|
||||
use crate::scan_error::ScanError;
|
||||
|
||||
mod extension_set;
|
||||
mod findings;
|
||||
mod formats;
|
||||
mod inspectors;
|
||||
|
@ -67,7 +64,7 @@ cfg_if! {
|
|||
fn main() {
|
||||
let args: parameters::Parameters = parameters::Parameters::parse();
|
||||
|
||||
let mut builder = env_logger::Builder::from_env(Env::new().filter_or("RUST_LOG", "INFO"));
|
||||
let mut builder = env_logger::Builder::from_env(Env::new().filter_or("RUST_LOG", args.default_verbosity()));
|
||||
|
||||
builder
|
||||
// .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args()))
|
||||
|
@ -115,13 +112,13 @@ fn main() {
|
|||
match result {
|
||||
Ok(r) => {
|
||||
debug!(
|
||||
"{:?} is {}, should have file extension {}",
|
||||
"{:?} is of type {}, should have extension \"{}\"",
|
||||
r.file,
|
||||
r.mime,
|
||||
r.recommended_extension().unwrap_or_else(|| "???".into())
|
||||
)
|
||||
}
|
||||
Err(f) => warn!("Error 0uo - {}", f),
|
||||
Err(f) => warn!("{}", f),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,14 +220,12 @@ fn scan_file(entry: &DirEntry) -> Result<Findings, ScanError> {
|
|||
|
||||
if result.is_err() {
|
||||
// an error occurred while trying to read the file
|
||||
// error!("{}: {}", entry.path().to_string_lossy(), error);
|
||||
return Err(ScanError::File(entry.path()));
|
||||
}
|
||||
|
||||
let result = result.unwrap();
|
||||
if result.is_none() {
|
||||
// the file was read successfully, but we were unable to determine its mimetype
|
||||
// warn!("Couldn't determine mimetype for {}", entry.path().to_string_lossy());
|
||||
return Err(ScanError::Mime(entry.path()));
|
||||
}
|
||||
|
||||
|
@ -259,6 +254,8 @@ fn scan_file(entry: &DirEntry) -> Result<Findings, ScanError> {
|
|||
fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "multi-threaded")] {
|
||||
use rayon::prelude::*;
|
||||
|
||||
// rather than using a standard par_iter, split the entries into chunks of 32 first.
|
||||
// this allows each spawned thread to handle 32 files before before closing, rather than creating a new thread for
|
||||
// each file. this leads to a pretty substantial speedup that i'm pretty substantially happy about 0u0
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! [Clap] struct used to parse command line arguments.
|
||||
|
||||
use crate::extension_set::ExtensionSet;
|
||||
use crate::string_type::String as StringType;
|
||||
use cfg_if::cfg_if;
|
||||
use clap::{AppSettings, Clap};
|
||||
|
@ -72,6 +71,11 @@ pub struct Parameters {
|
|||
#[clap(short, long)]
|
||||
pub follow_symlinks: bool,
|
||||
|
||||
/// Output verbosity. Defaults to only logging warnings and errors.
|
||||
/// Can be overridden by RUST_LOG.
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
pub verbose: u8,
|
||||
|
||||
/// The directory to process.
|
||||
// TODO: right now this can only take a single directory - should this be improved?
|
||||
#[clap(name = "DIR", default_value = ".", parse(from_os_str))]
|
||||
|
@ -110,4 +114,67 @@ impl Parameters {
|
|||
follow_symlinks: self.follow_symlinks,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_verbosity(&self) -> &'static str {
|
||||
#![allow(clippy::missing_const_for_fn)]
|
||||
// match was not permitted inside const functions until 1.46
|
||||
|
||||
match self.verbose {
|
||||
0 => "warn",
|
||||
1 => "info",
|
||||
2 => "debug",
|
||||
_ => "trace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets of extensions for use with [Parameter](crate::parameters::Parameters)'s `-E` flag.
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
pub enum ExtensionSet {
|
||||
/// Extensions used for image file formats, such as `png`, `jpeg`, `webp`, etc.
|
||||
Images,
|
||||
/// Extensions used for audio file formats, such as `mp3`, `ogg`, `flac`, etc.
|
||||
Audio,
|
||||
/// Extensions used for video file formats, such as `mkv`, `mp4`, `mov`, etc.
|
||||
Videos,
|
||||
/// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images),
|
||||
/// [Audio](ExtensionSet::Audio) and [Videos](ExtensionSet::Videos) variants.
|
||||
Media,
|
||||
/// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc.
|
||||
Documents,
|
||||
/// Extensions used for text file formats, such as `txt`, `toml`, `html`, etc.
|
||||
Text,
|
||||
/// Extensions used for archive file formats, such as `zip`, `zst`, `gz`, etc.
|
||||
Archives,
|
||||
/// Extensions used for system file formats, such as `mbr`, `crash`, `dll`, etc.
|
||||
System,
|
||||
}
|
||||
|
||||
impl ExtensionSet {
|
||||
/// The list of known extensions for this `ExtensionSet`.
|
||||
pub fn extensions(&self) -> Vec<&str> {
|
||||
match self {
|
||||
Self::Images => mime_guess::get_mime_extensions_str("image/*").unwrap().to_vec(),
|
||||
Self::Audio => mime_guess::get_mime_extensions_str("audio/*").unwrap().to_vec(),
|
||||
Self::Videos => mime_guess::get_mime_extensions_str("video/*").unwrap().to_vec(),
|
||||
Self::Media => [
|
||||
Self::Images.extensions(),
|
||||
Self::Audio.extensions(),
|
||||
Self::Videos.extensions(),
|
||||
]
|
||||
.concat(),
|
||||
Self::Documents => vec![
|
||||
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
||||
"pages", "key", "numbers",
|
||||
],
|
||||
Self::Text => mime_guess::get_mime_extensions_str("text/*").unwrap().to_vec(),
|
||||
// many compressed file types follow the name scheme "application/x.+compressed.*" - maybe this can be used
|
||||
// somehow to extract extensions for compressed files from mime_guess?
|
||||
Self::Archives => vec!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz", "rpa"],
|
||||
Self::System => vec![
|
||||
"com", "dll", "exe", "sys", "reg", "nt", "cpl", "msi", "efi", "bio", "rcv", "mbr", "sbf", "grub", "ko",
|
||||
"dylib", "pdb", "hdmp", "crash",
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ fn outputs_move_commands() {
|
|||
#[test]
|
||||
/// Ensure that the Media extension set contains all (is a superset) of Audio, Video, and Images.
|
||||
fn media_contains_audio_video_images() {
|
||||
use crate::extension_set::ExtensionSet::{Audio, Images, Media, Videos};
|
||||
use crate::parameters::ExtensionSet::{Audio, Images, Media, Videos};
|
||||
let media_exts = Media.extensions();
|
||||
|
||||
// assert every extension in the audio/video/image sets is contained in the media set
|
||||
|
|
6
test.py
6
test.py
|
@ -11,7 +11,7 @@ def test_archs():
|
|||
|
||||
for arch in archs:
|
||||
print(f"Testing {arch} ({upto} of {target})")
|
||||
subprocess.run(f"cross test --features=infer-backend --target {arch}-unknown-linux-gnu".split(" "))
|
||||
subprocess.run(f"cross test --features=infer-backend --target {arch}-unknown-linux-gnu".split(" ")).check_returncode()
|
||||
upto += 1
|
||||
|
||||
def test_versions():
|
||||
|
@ -33,11 +33,11 @@ def test_versions():
|
|||
for version in versions:
|
||||
for backend in backends:
|
||||
print(f"[{version}, {backend}] Tests ({upto} of {target})")
|
||||
subprocess.run(f"cargo +{version} test --features={backend}-backend".split(" "))
|
||||
subprocess.run(f"cargo +{version} test --features={backend}-backend".split(" ")).check_returncode()
|
||||
upto += 1
|
||||
|
||||
print(f"[{version}, {backend}] Scanning imgs ({upto} of {target})")
|
||||
subprocess.run(f"cargo +{version} run --release --features={backend}-backend -- imgs".split(" "))
|
||||
subprocess.run(f"cargo +{version} run --release --features={backend}-backend -- imgs".split(" ")).check_returncode()
|
||||
upto += 1
|
||||
|
||||
def main():
|
||||
|
|
Loading…
Reference in a new issue