Compare commits
5 commits
2713083a82
...
4d553587aa
Author | SHA1 | Date | |
---|---|---|---|
4d553587aa | |||
255665cae0 | |||
b877d7d65e | |||
dc2ac7b002 | |||
83091c754d |
10 changed files with 73 additions and 39 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -2,12 +2,16 @@
|
||||||
Dates are given in YYYY-MM-DD format.
|
Dates are given in YYYY-MM-DD format.
|
||||||
|
|
||||||
## v0.2
|
## v0.2
|
||||||
### v0.2.12 (2021-???)
|
### v0.2.13 (2021-???)
|
||||||
- Added Apple iWork document formats to documents extension set
|
- Added Apple iWork document formats to documents extension set
|
||||||
- Cleaned up and properly documented tests
|
- Cleaned up and properly documented tests
|
||||||
- Renamed `Script` (in `formats.rs`) to `Shell`, in line with renaming in `paramaters.rs`
|
- Renamed `Script` (in `formats.rs`) to `Shell`, in line with renaming in `parameters.rs`
|
||||||
- Added .rpa (renpy archive) support
|
- Added .rpa (Ren'Py archive) support to infer backend
|
||||||
- Added system extension set
|
- Added system extension set
|
||||||
|
- [`xdg-mime`] no longer uses git version
|
||||||
|
- Output is 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
|
||||||
|
- Fixed some bad formatting in PowerShell output
|
||||||
|
|
||||||
### v0.2.12 (2021-04-14)
|
### v0.2.12 (2021-04-14)
|
||||||
#### Features
|
#### Features
|
||||||
|
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -202,6 +202,7 @@ dependencies = [
|
||||||
"exitcode",
|
"exitcode",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"infer",
|
"infer",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -282,6 +283,15 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -742,7 +752,8 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xdg-mime"
|
name = "xdg-mime"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "git+https://github.com/ebassi/xdg-mime-rs?rev=de5a6dd#de5a6dd04b1a225894c51b5c6e5f5a22ffa46373"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87bf7b69bb50588d70a36e467be29d3df3e8c32580276d62eded9738c1a797aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"glob",
|
"glob",
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -33,16 +33,15 @@ infer = "0.4.0"
|
||||||
rayon = { version = "1.5.0", optional = true }
|
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"
|
||||||
|
itertools = "0.10.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
xdg-mime = "0.3.2"
|
xdg-mime = "0.3.3"
|
||||||
|
|
||||||
[target.'cfg(not(all(target_endian = "big", target_pointer_width = "32")))'.dependencies]
|
[target.'cfg(not(all(target_endian = "big", target_pointer_width = "32")))'.dependencies]
|
||||||
smartstring = "0.2.6"
|
smartstring = "0.2.6"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# use git version while waiting on a release incorporating https://github.com/ebassi/xdg-mime-rs/commit/de5a6dd
|
|
||||||
xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs", version = "0.3", rev = "de5a6dd" }
|
|
||||||
# forked version with many more mime types
|
# forked version with many more mime types
|
||||||
mime_guess = { git = "https://github.com/Lynnesbian/mime_guess", version = "2.0.4" }
|
mime_guess = { git = "https://github.com/Lynnesbian/mime_guess", version = "2.0.4" }
|
||||||
|
|
||||||
|
@ -70,6 +69,9 @@ lto = "thin"
|
||||||
[profile.test]
|
[profile.test]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|
||||||
# optimise dependencies, even when producing debug builds
|
# optimise dependencies, even when producing debug and test builds
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
[profile.test.package."*"]
|
||||||
|
opt-level = 3
|
||||||
|
|
|
@ -43,9 +43,10 @@ impl ExtensionSet {
|
||||||
// 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!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz", "rpa"],
|
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",
|
Self::System => vec![
|
||||||
|
"com", "dll", "exe", "sys", "reg", "nt", "cpl", "msi", "efi", "bio", "rcv", "mbr", "sbf", "grub", "ko",
|
||||||
|
"dylib", "pdb", "hdmp", "crash",
|
||||||
],
|
],
|
||||||
Self::Archives => vec!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz"],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::string_type::String;
|
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
|
|
||||||
use crate::inspectors::mime_extension_lookup;
|
use crate::inspectors::mime_extension_lookup;
|
||||||
|
use crate::string_type::String;
|
||||||
|
|
||||||
/// Information about a scanned file.
|
/// Information about a scanned file.
|
||||||
|
#[derive(Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct Findings<'a> {
|
pub struct Findings<'a> {
|
||||||
/// The location of the scanned file.
|
/// The location of the scanned file.
|
||||||
pub file: &'a Path,
|
pub file: &'a Path,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! The various formats that [fif](crate) can output to.
|
//! The various formats that [fif](crate) can output to.
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
@ -9,7 +10,7 @@ use snailquote::escape;
|
||||||
|
|
||||||
use crate::scan_error::ScanError;
|
use crate::scan_error::ScanError;
|
||||||
use crate::{Findings, BACKEND};
|
use crate::{Findings, BACKEND};
|
||||||
use std::ffi::OsStr;
|
use itertools::Itertools;
|
||||||
|
|
||||||
/// 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");
|
||||||
|
@ -83,18 +84,23 @@ pub trait Format {
|
||||||
// TODO: clean this up - it's kinda messy
|
// TODO: clean this up - it's kinda messy
|
||||||
self.header(entries, f)?;
|
self.header(entries, f)?;
|
||||||
|
|
||||||
for entry in entries {
|
// output will be generated in the order:
|
||||||
match entry {
|
// - files that couldn't be read
|
||||||
Ok(finding) => {
|
// - files with no known mime type
|
||||||
if let Some(ext) = finding.recommended_extension() {
|
// - files with no known extension
|
||||||
self.rename(f, finding.file, &finding.file.with_extension(ext.as_str()))?
|
// - files with a known extension
|
||||||
} else {
|
// files that already have a correct extension won't be represented in the output.
|
||||||
self.no_known_extension(f, finding.file)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(error) => {
|
// sort errors so unreadable files appear before files with unknown mimetypes - ScanError impls Ord such that
|
||||||
// something went wrong 0uo
|
// ScanError::File > ScanError::Mime
|
||||||
|
let errors = entries.iter().filter_map(|e| e.as_ref().err()).sorted();
|
||||||
|
// sort files so that files with no known extension come before those with known extensions - None > Some("jpg")
|
||||||
|
let findings = entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| e.as_ref().ok())
|
||||||
|
.sorted_by(|a, b| b.recommended_extension().cmp(&a.recommended_extension()).reverse());
|
||||||
|
|
||||||
|
for error in errors {
|
||||||
match error {
|
match error {
|
||||||
// failed to read the file
|
// failed to read the file
|
||||||
ScanError::File(path) => self.unreadable(f, path)?,
|
ScanError::File(path) => self.unreadable(f, path)?,
|
||||||
|
@ -102,6 +108,12 @@ pub trait Format {
|
||||||
ScanError::Mime(path) => self.unknown_type(f, path)?,
|
ScanError::Mime(path) => self.unknown_type(f, path)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for finding in findings {
|
||||||
|
if let Some(ext) = finding.recommended_extension() {
|
||||||
|
self.rename(f, finding.file, &finding.file.with_extension(ext.as_str()))?
|
||||||
|
} else {
|
||||||
|
self.no_known_extension(f, finding.file)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +226,7 @@ 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(
|
smart_write(
|
||||||
f,
|
f,
|
||||||
&["<# Failed to detect mime type for ".into(), path.into(), "#>".into()],
|
&["<# Failed to detect mime type for ".into(), path.into(), "#>".into(), Writable::Newline],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,8 +91,6 @@ cached! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
match exts {
|
match exts {
|
||||||
Some(exts) => {
|
Some(exts) => {
|
||||||
let possible_exts: Vec<String> = exts.iter().map(|e| String::from(*e)).collect();
|
let possible_exts: Vec<String> = exts.iter().map(|e| String::from(*e)).collect();
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -17,11 +17,14 @@
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![warn(trivial_casts, unused_lifetimes, unused_qualifications)]
|
#![warn(trivial_casts, unused_lifetimes, unused_qualifications)]
|
||||||
|
|
||||||
use std::io::{stdout, BufWriter};
|
use std::ffi::OsStr;
|
||||||
|
use std::io::{stdout, BufWriter, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
|
use env_logger::Env;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
#[cfg(feature = "multi-threaded")]
|
#[cfg(feature = "multi-threaded")]
|
||||||
|
@ -33,9 +36,6 @@ use crate::formats::{Format, PowerShell, Shell};
|
||||||
use crate::mime_db::MimeDb;
|
use crate::mime_db::MimeDb;
|
||||||
use crate::parameters::{OutputFormat, ScanOpts};
|
use crate::parameters::{OutputFormat, ScanOpts};
|
||||||
use crate::scan_error::ScanError;
|
use crate::scan_error::ScanError;
|
||||||
use env_logger::Env;
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
mod extension_set;
|
mod extension_set;
|
||||||
mod findings;
|
mod findings;
|
||||||
|
@ -143,6 +143,11 @@ fn main() {
|
||||||
exit(exitcode::IOERR);
|
exit(exitcode::IOERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if buffered_stdout.flush().is_err() {
|
||||||
|
error!("Failed to flush stdout.");
|
||||||
|
exit(exitcode::IOERR);
|
||||||
|
}
|
||||||
|
|
||||||
debug!("Done");
|
debug!("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter, Result};
|
use std::fmt::{Display, Formatter, Result};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
|
||||||
pub enum ScanError<'a> {
|
pub enum ScanError<'a> {
|
||||||
/// Something went wrong while trying to read the given file.
|
/// Something went wrong while trying to read the given file.
|
||||||
File(&'a Path),
|
File(&'a Path),
|
||||||
|
|
|
@ -192,7 +192,7 @@ fn argument_parsing() {
|
||||||
ScanOpts {
|
ScanOpts {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
extensionless: false,
|
extensionless: false,
|
||||||
follow_symlinks: true
|
follow_symlinks: true,
|
||||||
},
|
},
|
||||||
"ScanOpts are incorrect"
|
"ScanOpts are incorrect"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue