Compare commits
No commits in common. "090e4adafe3ae4bf6ec0dc1a9147a9e2e9cacaff" and "534101db8b1033371dbb35cdd6f1c242729d9acf" have entirely different histories.
090e4adafe
...
534101db8b
14 changed files with 113 additions and 149 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,4 +8,4 @@ fif_*
|
||||||
!clippy.sh
|
!clippy.sh
|
||||||
cargo-timing*.html
|
cargo-timing*.html
|
||||||
todo.txt
|
todo.txt
|
||||||
/pkg/*
|
/pkg/fif.spec
|
|
@ -62,22 +62,18 @@ build-nightly:
|
||||||
|
|
||||||
test-stable:
|
test-stable:
|
||||||
extends: .cargo-test
|
extends: .cargo-test
|
||||||
needs: ["build-stable"]
|
|
||||||
|
|
||||||
test-msrv:
|
test-msrv:
|
||||||
extends: test-stable
|
extends: test-stable
|
||||||
image: "rust:1.43.0"
|
image: "rust:1.43.0"
|
||||||
needs: ["build-msrv"]
|
|
||||||
|
|
||||||
test-nightly:
|
test-nightly:
|
||||||
extends: test-stable
|
extends: test-stable
|
||||||
image: "rustlang/rust:nightly"
|
image: "rustlang/rust:nightly"
|
||||||
needs: ["build-nightly"]
|
|
||||||
|
|
||||||
# VERSION
|
# VERSION
|
||||||
|
|
||||||
fif-version:
|
fif-version:
|
||||||
stage: version
|
stage: version
|
||||||
needs: ["build-stable"]
|
|
||||||
script:
|
script:
|
||||||
cargo run -- -V
|
cargo run -- -V
|
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,8 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
Dates are given in YYYY-MM-DD format.
|
Dates are given in YYYY-MM-DD format.
|
||||||
|
|
||||||
## v0.3
|
## v0.2
|
||||||
### v0.3.0 (2021-04-28)
|
### v0.2.14 (2021-xx-yy)
|
||||||
#### Features
|
#### Features
|
||||||
- Added `-x`/`--exclude` flag for excluding file extensions (overrides `-e` or `-E` - `-E images -x jpg` scans all image
|
- Added `-x`/`--exclude` flag for excluding file extensions (overrides `-e` or `-E` - `-E images -x jpg` scans all image
|
||||||
files, except ".jpg" files)
|
files, except ".jpg" files)
|
||||||
|
@ -12,19 +12,12 @@ Dates are given in YYYY-MM-DD format.
|
||||||
- `-e` and `-E` no longer conflict with each other, and can now be used together. For example, `-E images -e mp3`
|
- `-e` and `-E` no longer conflict with each other, and can now be used together. For example, `-E images -e mp3`
|
||||||
will scan all images *and* all MP3 files
|
will scan all images *and* all MP3 files
|
||||||
- It is now possible to specify multiple extension sets at once: `-E images,system` will scan all images and archives
|
- It is now possible to specify multiple extension sets at once: `-E images,system` will scan all images and archives
|
||||||
- fif's output now includes the directory it was run from
|
|
||||||
- Added `-q`/`--quiet` flag for reducing output verbosity
|
|
||||||
#### Bugfixes
|
|
||||||
- Resolved some discrepancies between `application/xml` and `text/xml`
|
|
||||||
#### Other
|
#### Other
|
||||||
- Published 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)
|
[crates.io](https://crates.io)
|
||||||
- The `videos` extension set has been renamed to `video`, in line with `audio`. `fif --help` has actually mistakenly
|
- The `videos` extension set has been renamed to `video`, in line with `audio`. `fif --help` has actually mistakenly
|
||||||
referred to the set as `video` since v0.2.12! 0uo
|
referred to the set as `video` since v0.2.12! 0uo
|
||||||
- CI has been vastly improved
|
|
||||||
- Changed default verbosity to `info`
|
|
||||||
|
|
||||||
## v0.2
|
|
||||||
### v0.2.13 (2021-04-26)
|
### v0.2.13 (2021-04-26)
|
||||||
#### Features
|
#### Features
|
||||||
- Added `-v`/`--verbose` flag for setting verbosity without using `RUST_LOG`
|
- Added `-v`/`--verbose` flag for setting verbosity without using `RUST_LOG`
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -193,7 +193,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fif"
|
name = "fif"
|
||||||
version = "0.3.0"
|
version = "0.2.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cached",
|
"cached",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
|
@ -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.3.0"
|
version = "0.2.13"
|
||||||
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"
|
||||||
|
|
40
README.md
40
README.md
|
@ -1,10 +1,5 @@
|
||||||
<div align="center">fif</div>
|
fif
|
||||||
===
|
===
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
![A screenshot demonstrating fif's ability to detect the correct file extensions for a few files.
|
|
||||||
](https://gitlab.com/Lynnesbian/fif/-/blob/master/doc/screenshot.png)
|
|
||||||
|
|
||||||
[![Version](https://img.shields.io/crates/v/fif.svg?logo=rust&style=flat-square)
|
[![Version](https://img.shields.io/crates/v/fif.svg?logo=rust&style=flat-square)
|
||||||
](https://crates.io/crates/fif)
|
](https://crates.io/crates/fif)
|
||||||
[![Minimum Supported Rust Version](https://img.shields.io/badge/msrv-1.43.0-orange?logo=rust&style=flat-square)
|
[![Minimum Supported Rust Version](https://img.shields.io/badge/msrv-1.43.0-orange?logo=rust&style=flat-square)
|
||||||
|
@ -18,8 +13,7 @@
|
||||||
[![Unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)
|
[![Unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)
|
||||||
](https://github.com/rust-secure-code/safety-dance/)
|
](https://github.com/rust-secure-code/safety-dance/)
|
||||||
|
|
||||||
*A command-line tool for detecting and optionally correcting files with incorrect extensions.*
|
A command-line tool for detecting and optionally correcting files with incorrect extensions.
|
||||||
</div>
|
|
||||||
|
|
||||||
fif recursively scans the given directory and outputs a shell script to fix the name of any files with incorrect
|
fif recursively scans the given directory and outputs a shell script to fix the name of any files with incorrect
|
||||||
extensions. By default, fif will scan all non-hidden files in the given directory, and will ignore symlinks.
|
extensions. By default, fif will scan all non-hidden files in the given directory, and will ignore symlinks.
|
||||||
|
@ -35,8 +29,8 @@ that it's not doing anything that will give you a headache before running it.
|
||||||
cargo install --locked fif
|
cargo install --locked fif
|
||||||
```
|
```
|
||||||
|
|
||||||
To update, simply re-run this command, or use a tool like [cargo-update
|
To update, simply re-run this command, or use a tool like
|
||||||
](https://github.com/nabijaczleweli/cargo-update).
|
[cargo-update](https://github.com/nabijaczleweli/cargo-update).
|
||||||
|
|
||||||
#### Other backends
|
#### Other backends
|
||||||
`fif` supports using [`infer`](https://crates.io/crates/infer) or [`xdg-mime`](https://crates.io/crates/xdg-mime) as its
|
`fif` supports using [`infer`](https://crates.io/crates/infer) or [`xdg-mime`](https://crates.io/crates/xdg-mime) as its
|
||||||
|
@ -106,35 +100,23 @@ fif -O powershell ~/Documents > output.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
By default, fif will log any info, warnings, and errors encountered during execution. This can be changed with the `-v`
|
By default, fif will log any warnings and/or errors encountered during execution. This can be changed with the `-v`
|
||||||
flag:
|
flag:
|
||||||
```bash
|
```bash
|
||||||
# also log debug info
|
# also log info
|
||||||
fif -v ~/Downloads
|
fif -v ~/Downloads
|
||||||
# ...and trace info
|
# ...and debug
|
||||||
fif -vv ~/Downloads
|
fif -vv ~/Downloads
|
||||||
|
# ...and trace
|
||||||
|
fif -vvv ~/Downloads
|
||||||
```
|
```
|
||||||
|
The verbosity of the logging can be
|
||||||
You can also reduce the level of logging with the `-q` flag:
|
modified by the `RUST_LOG` to one of: `trace`, `debug`, `info`, `warn`, `error`.
|
||||||
```bash
|
|
||||||
# don't show info
|
|
||||||
fif -q ~/Downloads
|
|
||||||
# ...or warnings
|
|
||||||
fif -qq ~/Downloads
|
|
||||||
# ...or even errors!
|
|
||||||
fif -qqq ~/Downloads
|
|
||||||
```
|
|
||||||
|
|
||||||
The verbosity of the logging can also be modified by setting the environment variable `RUST_LOG` to `off`, `trace`,
|
|
||||||
`debug`, `info`, `warn`, or `error`. Values set by `RUST_LOG` override the `-v` and `-q` flags.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# show all levels except trace
|
|
||||||
RUST_LOG=debug fif ~/Downloads
|
RUST_LOG=debug fif ~/Downloads
|
||||||
# only show errors
|
|
||||||
RUST_LOG=error fif ~/Downloads
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The five logging levels are used as follows:
|
The five logging levels are used as follows:
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
26
pkg/PKGBUILD
Normal file
26
pkg/PKGBUILD
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
pkgname=fif
|
||||||
|
pkgver=0.2.11
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="A command-line tool for detecting and optionally correcting files with incorrect extensions."
|
||||||
|
# tier 1 rust linux targets
|
||||||
|
arch=('x86_64' 'i686' 'aarch64')
|
||||||
|
url="https://git.bune.city/lynnesbian/fif"
|
||||||
|
license=('GPLv3+')
|
||||||
|
depends=('shared-mime-info')
|
||||||
|
source=("$pkgname-$pkgver.tar.gz::https://git.bune.city/lynnesbian/$pkgname/archive/v${pkgver}.tar.gz")
|
||||||
|
sha256sums=("fd2b3133fabf8ad1993c6d16a9bf1ad645b1eff8fd30a4a9227ef5a157f56183")
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$pkgname"
|
||||||
|
cargo build --release --locked
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
cd "$pkgname"
|
||||||
|
cargo build --release --locked
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$pkgname"
|
||||||
|
install -Dm 755 target/release/${pkgname} -t "${pkgdir}/usr/bin"
|
||||||
|
}
|
|
@ -18,6 +18,6 @@ pub struct Findings<'a> {
|
||||||
|
|
||||||
impl<'a> Findings<'a> {
|
impl<'a> Findings<'a> {
|
||||||
pub fn recommended_extension(&self) -> Option<String> {
|
pub fn recommended_extension(&self) -> Option<String> {
|
||||||
mime_extension_lookup(self.mime.clone()).map(|extensions| extensions[0].clone())
|
mime_extension_lookup(self.mime.clone()).map(|extensions| extensions[0].to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,6 @@ macro_rules! writables {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
/// Does the same thing as [writables], but adds a Newline to the end.
|
|
||||||
macro_rules! writablesln {
|
|
||||||
[$($args:tt),+] => {
|
|
||||||
&[$(writables!(@do $args),)* writables!(@do Newline)]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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");
|
||||||
|
|
||||||
|
@ -138,12 +130,12 @@ pub trait Format {
|
||||||
fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
||||||
fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
||||||
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()>;
|
||||||
fn header<W: Write>(&self, f: &mut W, entries: &Entries) -> io::Result<()>;
|
fn header<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
|
||||||
fn footer<W: Write>(&self, f: &mut W, entries: &Entries) -> io::Result<()>;
|
fn footer<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
|
||||||
|
|
||||||
fn write_all<W: Write>(&self, f: &mut W, entries: &Entries) -> io::Result<()> {
|
fn write_all<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()> {
|
||||||
// TODO: clean this up - it's kinda messy
|
// TODO: clean this up - it's kinda messy
|
||||||
self.header(f, entries)?;
|
self.header(entries, f)?;
|
||||||
|
|
||||||
// output will be generated in the order:
|
// output will be generated in the order:
|
||||||
// - files that couldn't be read
|
// - files that couldn't be read
|
||||||
|
@ -178,7 +170,7 @@ pub trait Format {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.footer(f, entries)
|
self.footer(entries, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,36 +183,38 @@ impl Format for Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename<W: Write>(&self, f: &mut W, from: &Path, to: &Path) -> io::Result<()> {
|
fn rename<W: Write>(&self, f: &mut W, from: &Path, to: &Path) -> io::Result<()> {
|
||||||
smart_write(f, writablesln!("mv -v -i -- ", from, Space, to))
|
smart_write(f, writables!("mv -v -i -- ", from, Space, to, Newline))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
fn no_known_extension<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
||||||
smart_write(f, writablesln!["echo No known extension for ", path])
|
smart_write(f, writables!["echo No known extension for ", path, Newline])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
fn unreadable<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
||||||
smart_write(f, writablesln!["# Failed to read", path])
|
smart_write(f, writables!["# Failed to read", path, Newline])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
||||||
smart_write(f, writablesln!["# Failed to detect mime type for ", path])
|
smart_write(f, writables!["# Failed to detect mime type for ", path, Newline])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header<W: Write>(&self, f: &mut W, _: &Entries) -> io::Result<()> {
|
fn header<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
||||||
smart_write(
|
smart_write(
|
||||||
f,
|
f,
|
||||||
writablesln!["#!/usr/bin/env sh", Newline, "# ", (generated_by().as_str())],
|
writables![
|
||||||
)?;
|
"#!/usr/bin/env sh",
|
||||||
|
Newline,
|
||||||
if let Ok(working_directory) = std::env::current_dir() {
|
"# ",
|
||||||
smart_write(f, writablesln!["# Run from ", (working_directory.as_path())])?;
|
(generated_by().as_str()),
|
||||||
|
Newline,
|
||||||
|
"set -e",
|
||||||
|
Newline
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
smart_write(f, writablesln![Newline, "set -e", Newline])
|
fn footer<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
||||||
}
|
smart_write(f, writables![Newline, "echo 'Done.'", Newline])
|
||||||
|
|
||||||
fn footer<W: Write>(&self, f: &mut W, _: &Entries) -> io::Result<()> {
|
|
||||||
smart_write(f, writablesln![Newline, "echo 'Done.'"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +234,13 @@ impl Format for PowerShell {
|
||||||
// there doesn't seem to be a way to rename the file, prompting only if the target already exists.
|
// there doesn't seem to be a way to rename the file, prompting only if the target already exists.
|
||||||
smart_write(
|
smart_write(
|
||||||
f,
|
f,
|
||||||
writablesln!["Rename-Item -Path ", from, " -NewName ", (to.file_name().unwrap())],
|
writables![
|
||||||
|
"Rename-Item -Path ",
|
||||||
|
from,
|
||||||
|
" -NewName ",
|
||||||
|
(to.file_name().unwrap()),
|
||||||
|
Newline
|
||||||
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,23 +266,27 @@ impl Format for PowerShell {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
fn unknown_type<W: Write>(&self, f: &mut W, path: &Path) -> io::Result<()> {
|
||||||
smart_write(f, writablesln!["<# Failed to detect mime type for ", path, " #>"])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn header<W: Write>(&self, f: &mut W, _: &Entries) -> io::Result<()> {
|
|
||||||
smart_write(
|
smart_write(
|
||||||
f,
|
f,
|
||||||
writablesln!["#!/usr/bin/env pwsh", Newline, "<# ", (generated_by().as_str()), " #>"],
|
writables!["<# Failed to detect mime type for ", path, " #>", Newline],
|
||||||
)?;
|
)
|
||||||
|
|
||||||
if let Ok(working_directory) = std::env::current_dir() {
|
|
||||||
smart_write(f, writablesln!["<# Run from ", (working_directory.as_path()), " #>"])?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
smart_write(f, writables![Newline])
|
fn header<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
||||||
|
smart_write(
|
||||||
|
f,
|
||||||
|
writables![
|
||||||
|
"#!/usr/bin/env pwsh",
|
||||||
|
Newline,
|
||||||
|
"<# ",
|
||||||
|
(generated_by().as_str()),
|
||||||
|
" #>",
|
||||||
|
Newline
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn footer<W: Write>(&self, f: &mut W, _: &Entries) -> io::Result<()> {
|
fn footer<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
||||||
smart_write(f, writablesln![Newline, "Write-Output 'Done!'"])
|
smart_write(f, writables![Newline, "Write-Output 'Done!'", Newline])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,10 @@ pub fn mime_type<T: MimeDb>(db: &T, path: &Path) -> io::Result<Option<Mime>> {
|
||||||
|
|
||||||
// TODO: avoid cloning mime if possible, although i don't really see how it would be - maybe instead of passing the mime
|
// TODO: avoid cloning mime if possible, although i don't really see how it would be - maybe instead of passing the mime
|
||||||
// object, pass a hash of it?
|
// object, pass a hash of it?
|
||||||
|
|
||||||
cached! {
|
cached! {
|
||||||
MIMEXT;
|
MIMEXT;
|
||||||
fn mime_extension_lookup(mime: Mime) -> Option<Vec<String>> = {
|
fn mime_extension_lookup(mime: Mime) -> Option<Vec<String>> = {
|
||||||
|
|
||||||
// Returns a list of known extensions for this mime type, if any.
|
// Returns a list of known extensions for this mime type, if any.
|
||||||
// ↑ this is supposed to be a doc comment, but the cached! macro doesn't support that... maybe i should switch to
|
// ↑ this is supposed to be a doc comment, but the cached! macro doesn't support that... maybe i should switch to
|
||||||
// the derive macro
|
// the derive macro
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -74,7 +74,6 @@ fn main() {
|
||||||
// .target(env_logger::Target::Stdout) // log to stdout rather than stderr
|
// .target(env_logger::Target::Stdout) // log to stdout rather than stderr
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
trace!("Initialise mimetype database");
|
|
||||||
init_db();
|
init_db();
|
||||||
|
|
||||||
debug!("Iterating directory: {:?}", args.dirs);
|
debug!("Iterating directory: {:?}", args.dirs);
|
||||||
|
@ -122,8 +121,6 @@ fn main() {
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
trace!("Scanning complete");
|
|
||||||
|
|
||||||
for result in &results {
|
for result in &results {
|
||||||
match result {
|
match result {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
|
@ -146,8 +143,8 @@ fn main() {
|
||||||
let mut buffered_stdout = BufWriter::new(stdout());
|
let mut buffered_stdout = BufWriter::new(stdout());
|
||||||
|
|
||||||
let result = match args.output_format {
|
let result = match args.output_format {
|
||||||
OutputFormat::Sh => Shell::new().write_all(&mut buffered_stdout, &results),
|
OutputFormat::Sh => Shell::new().write_all(&results, &mut buffered_stdout),
|
||||||
OutputFormat::PowerShell => PowerShell::new().write_all(&mut buffered_stdout, &results),
|
OutputFormat::PowerShell => PowerShell::new().write_all(&results, &mut buffered_stdout),
|
||||||
OutputFormat::Text => todo!(),
|
OutputFormat::Text => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -279,11 +276,13 @@ fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
|
||||||
if #[cfg(feature = "multi-threaded")] {
|
if #[cfg(feature = "multi-threaded")] {
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
// split the entries into chunks of 32, and iterate over each chunk of entries in a separate thread
|
// 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
|
||||||
entries
|
entries
|
||||||
.par_chunks(32)
|
.par_chunks(32) // split into chunks of 32
|
||||||
.flat_map(|chunk| {
|
.flat_map(|chunk| {
|
||||||
chunk
|
chunk // return Vec<...> instead of Chunk<Vec<...>>
|
||||||
.iter() // iter over the chunk, which is a slice of DirEntry structs
|
.iter() // iter over the chunk, which is a slice of DirEntry structs
|
||||||
.map(|entry| scan_file(entry))
|
.map(|entry| scan_file(entry))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -349,7 +348,7 @@ fn scan_directory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
|
if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
|
||||||
|
|
|
@ -17,7 +17,6 @@ cfg_if! {
|
||||||
#[derive(Clap, PartialEq, Debug)]
|
#[derive(Clap, PartialEq, Debug)]
|
||||||
pub enum OutputFormat {
|
pub enum OutputFormat {
|
||||||
/// A Bourne shell compatible script.
|
/// A Bourne shell compatible script.
|
||||||
#[clap(alias = "shell", alias = "bash")]
|
|
||||||
Sh,
|
Sh,
|
||||||
/// A PowerShell script.
|
/// A PowerShell script.
|
||||||
#[clap(alias = "powershell")]
|
#[clap(alias = "powershell")]
|
||||||
|
@ -48,23 +47,23 @@ pub struct Parameters {
|
||||||
/// Only examine files with these extensions.
|
/// Only examine files with these extensions.
|
||||||
/// Multiple extensions can be specified by either using the flag multiple times (`-e jpg -e png -e gif`), or by
|
/// Multiple extensions can be specified by either using the flag multiple times (`-e jpg -e png -e gif`), or by
|
||||||
/// separating them with commas (`-e jpg,png,gif`).
|
/// separating them with commas (`-e jpg,png,gif`).
|
||||||
#[clap(short, long, use_delimiter = true, require_delimiter = true, value_name = "ext")]
|
#[clap(short, long, use_delimiter = true, require_delimiter = true)]
|
||||||
pub exts: Option<Vec<StringType>>,
|
pub exts: Option<Vec<StringType>>,
|
||||||
|
|
||||||
/// Use these preset lists of extensions as the search filter (comma-separated list).
|
/// Use these preset lists of extensions as the search filter (comma-separated list).
|
||||||
/// `media` includes all extensions from the `audio`, `video`, and `images` sets, making `-E media` equivalent to
|
/// `media` includes all extensions from the `audio`, `video`, and `images` sets, making `-E media` equivalent to
|
||||||
/// `-E audio,video,images`.
|
/// `-E audio,video,images`.
|
||||||
#[clap(short = 'E', long, arg_enum, use_delimiter = true, require_delimiter = true, value_name = "set")]
|
#[clap(short = 'E', long, arg_enum, use_delimiter = true, require_delimiter = true)]
|
||||||
pub ext_set: Vec<ExtensionSet>,
|
pub ext_set: Vec<ExtensionSet>,
|
||||||
|
|
||||||
/// Don't scan files with these extensions.
|
/// Don't scan files with these extensions.
|
||||||
/// This option takes precedence over extensions specified with `-e` or `-E`.
|
/// This option takes precedence over extensions specified with `-e` or `-E`.
|
||||||
#[clap(short = 'x', long, use_delimiter = true, require_delimiter = true, value_name = "ext")]
|
#[clap(short = 'x', long, use_delimiter = true, require_delimiter = true)]
|
||||||
pub exclude: Option<Vec<StringType>>,
|
pub exclude: Option<Vec<StringType>>,
|
||||||
|
|
||||||
/// Exclude files using a preset list of extensions.
|
/// Exclude files using a preset list of extensions.
|
||||||
/// This option takes precedence over extensions specified with `-e` or `-E`.
|
/// This option takes precedence over extensions specified with `-e` or `-E`.
|
||||||
#[clap(short = 'X', long, arg_enum, use_delimiter = true, require_delimiter = true, value_name = "set")]
|
#[clap(short = 'X', long, arg_enum, use_delimiter = true, require_delimiter = true)]
|
||||||
pub exclude_set: Vec<ExtensionSet>,
|
pub exclude_set: Vec<ExtensionSet>,
|
||||||
|
|
||||||
/// Don't skip hidden files and directories.
|
/// Don't skip hidden files and directories.
|
||||||
|
@ -81,22 +80,18 @@ pub struct Parameters {
|
||||||
|
|
||||||
/// Output format to use.
|
/// Output format to use.
|
||||||
/// By default, fif will output a PowerShell script on Windows, and a Bourne Shell script on other platforms.
|
/// By default, fif will output a PowerShell script on Windows, and a Bourne Shell script on other platforms.
|
||||||
#[clap(short, long, default_value = DEFAULT_FORMAT, arg_enum, value_name = "format")]
|
#[clap(short, long, default_value = DEFAULT_FORMAT, arg_enum)]
|
||||||
pub output_format: OutputFormat,
|
pub output_format: OutputFormat,
|
||||||
|
|
||||||
/// Follow symlinks.
|
/// Follow symlinks.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub follow_symlinks: bool,
|
pub follow_symlinks: bool,
|
||||||
|
|
||||||
/// Output verbosity. Each additional `-v` increases verbosity.
|
/// Output verbosity. Defaults to only logging warnings and errors.
|
||||||
/// Can be overridden by RUST_LOG.
|
/// Can be overridden by RUST_LOG.
|
||||||
#[clap(short, long, parse(from_occurrences), group = "verbosity")]
|
#[clap(short, long, parse(from_occurrences))]
|
||||||
pub verbose: u8,
|
pub verbose: u8,
|
||||||
|
|
||||||
/// Output quietness. Each additional `-q` decreases verbosity.
|
|
||||||
#[clap(short, long, parse(from_occurrences), group = "verbosity")]
|
|
||||||
pub quiet: u8,
|
|
||||||
|
|
||||||
/// The directory to process.
|
/// The directory to process.
|
||||||
// TODO: right now this can only take a single directory - should this be improved?
|
// TODO: right now this can only take a single directory - should this be improved?
|
||||||
#[clap(name = "DIR", default_value = ".", parse(from_os_str))]
|
#[clap(name = "DIR", default_value = ".", parse(from_os_str))]
|
||||||
|
@ -186,17 +181,11 @@ impl Parameters {
|
||||||
#![allow(clippy::missing_const_for_fn)]
|
#![allow(clippy::missing_const_for_fn)]
|
||||||
// match was not permitted inside const functions until 1.46
|
// match was not permitted inside const functions until 1.46
|
||||||
|
|
||||||
match self.quiet {
|
|
||||||
0 => {
|
|
||||||
match self.verbose {
|
match self.verbose {
|
||||||
0 => "info", // no verbosity flags specified
|
0 => "warn",
|
||||||
1 => "debug", // -v
|
1 => "info",
|
||||||
_ => "trace" // -vv...
|
2 => "debug",
|
||||||
}
|
_ => "trace",
|
||||||
},
|
|
||||||
1 => "warn", // -q
|
|
||||||
2 => "error", // -qq
|
|
||||||
_ => "off" // -qqq...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,11 +229,7 @@ impl ExtensionSet {
|
||||||
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
||||||
"pages", "key", "numbers",
|
"pages", "key", "numbers",
|
||||||
],
|
],
|
||||||
Self::Text => [
|
Self::Text => [mime_guess::get_mime_extensions_str("text/*").unwrap(), &["js", "pl", "csh", "sh", "bash", "zsh", "fish", "bat", "php"]].concat(),
|
||||||
mime_guess::get_mime_extensions_str("text/*").unwrap(),
|
|
||||||
&["js", "pl", "csh", "sh", "bash", "zsh", "fish", "bat", "php"],
|
|
||||||
]
|
|
||||||
.concat(),
|
|
||||||
// many compressed file types follow the name scheme "application/x.+compressed.*" - maybe this can be used
|
// 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?
|
// somehow to extract extensions for compressed files from mime_guess?
|
||||||
Self::Archives => vec![
|
Self::Archives => vec![
|
||||||
|
|
|
@ -339,7 +339,7 @@ fn outputs_move_commands() {
|
||||||
let mut contents = std::string::String::new();
|
let mut contents = std::string::String::new();
|
||||||
|
|
||||||
Shell::new()
|
Shell::new()
|
||||||
.write_all(&mut cursor, &entries)
|
.write_all(&entries, &mut cursor)
|
||||||
.expect("Failed to write to cursor");
|
.expect("Failed to write to cursor");
|
||||||
cursor.set_position(0);
|
cursor.set_position(0);
|
||||||
cursor
|
cursor
|
||||||
|
@ -387,24 +387,3 @@ fn writables_is_correct() {
|
||||||
writables!["henlo", (Path::new("henlo")), Newline, Space]
|
writables!["henlo", (Path::new("henlo")), Newline, Space]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// Test various combinations of verbosity flags.
|
|
||||||
fn verbosity() {
|
|
||||||
assert!(Parameters::try_parse_from(&["fif", "-q", "-v"]).is_err(), "Failed to reject usage of both -q and -v!");
|
|
||||||
|
|
||||||
let mut expected_results = HashMap::new();
|
|
||||||
expected_results.insert("-qqqqqqqq", "off");
|
|
||||||
expected_results.insert("-qqq", "off");
|
|
||||||
expected_results.insert("-qq", "error");
|
|
||||||
expected_results.insert("-q", "warn");
|
|
||||||
expected_results.insert("-s", "info");
|
|
||||||
expected_results.insert("-v", "debug");
|
|
||||||
expected_results.insert("-vv", "trace");
|
|
||||||
expected_results.insert("-vvv", "trace");
|
|
||||||
expected_results.insert("-vvvvvvvv", "trace");
|
|
||||||
|
|
||||||
for (flags, level) in expected_results {
|
|
||||||
assert_eq!(Parameters::parse_from(&["fif", flags]).default_verbosity(), level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue