diff --git a/CHANGELOG.md b/CHANGELOG.md index d711173..682cb93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ Dates are given in YYYY-MM-DD format. - Added .rpa (Ren'Py archive) support to infer backend - 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 ### v0.2.12 (2021-04-14) #### Features diff --git a/Cargo.lock b/Cargo.lock index eb29c54..c4c529a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,6 +202,7 @@ dependencies = [ "exitcode", "fastrand", "infer", + "itertools", "log", "mime_guess", "once_cell", @@ -282,6 +283,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index d335640..f4b32ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ infer = "0.4.0" rayon = { version = "1.5.0", optional = true } exitcode = "1.1.2" cfg-if = "1.0.0" +itertools = "0.10.0" [target.'cfg(unix)'.dependencies] xdg-mime = "0.3.3" @@ -68,6 +69,9 @@ lto = "thin" [profile.test] opt-level = 0 -# optimise dependencies, even when producing debug builds +# optimise dependencies, even when producing debug and test builds [profile.dev.package."*"] opt-level = 3 + +[profile.test.package."*"] +opt-level = 3 diff --git a/src/findings.rs b/src/findings.rs index a2e4c72..c24ebf4 100644 --- a/src/findings.rs +++ b/src/findings.rs @@ -6,6 +6,7 @@ use crate::inspectors::mime_extension_lookup; use crate::string_type::String; /// Information about a scanned file. +#[derive(Ord, PartialOrd, Eq, PartialEq)] pub struct Findings<'a> { /// The location of the scanned file. pub file: &'a Path, diff --git a/src/formats.rs b/src/formats.rs index b57cc2f..9c70d45 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -10,6 +10,7 @@ use snailquote::escape; use crate::scan_error::ScanError; use crate::{Findings, BACKEND}; +use itertools::Itertools; /// The current version of fif, as defined in Cargo.toml. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); @@ -83,25 +84,36 @@ pub trait Format { // TODO: clean this up - it's kinda messy self.header(entries, f)?; - for entry in entries { - match entry { - Ok(finding) => { - 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)? - } - } + // output will be generated in the order: + // - files that couldn't be read + // - files with no known mime type + // - files with no known extension + // - files with a known extension + // files that already have a correct extension won't be represented in the output. - Err(error) => { - // something went wrong 0uo - match error { - // failed to read the file - ScanError::File(path) => self.unreadable(f, path)?, - // file was read successfully, but we couldn't determine a mimetype - ScanError::Mime(path) => self.unknown_type(f, path)?, - } - } + // sort errors so unreadable files appear before files with unknown mimetypes - ScanError impls Ord such that + // 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 { + // failed to read the file + ScanError::File(path) => self.unreadable(f, path)?, + // file was read successfully, but we couldn't determine a mimetype + 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)? } } diff --git a/src/main.rs b/src/main.rs index ed8ae15..82d2cfb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ #![warn(trivial_casts, unused_lifetimes, unused_qualifications)] use std::ffi::OsStr; -use std::io::{stdout, BufWriter}; +use std::io::{stdout, BufWriter, Write}; use std::path::Path; use std::process::exit; @@ -130,8 +130,6 @@ fn main() { exit(0); } - // TODO: sort entries in order of: valid moves, unknown mime, unknown ext, error - let mut buffered_stdout = BufWriter::new(stdout()); let result = match args.output_format { @@ -145,6 +143,11 @@ fn main() { exit(exitcode::IOERR); } + if buffered_stdout.flush().is_err() { + error!("Failed to flush stdout."); + exit(exitcode::IOERR); + } + debug!("Done"); } diff --git a/src/scan_error.rs b/src/scan_error.rs index c4fc785..bf7ea8d 100644 --- a/src/scan_error.rs +++ b/src/scan_error.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter, Result}; use std::path::Path; -#[derive(Debug)] +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)] pub enum ScanError<'a> { /// Something went wrong while trying to read the given file. File(&'a Path),