nicer error handling, exit codes

This commit is contained in:
Lynne Megido 2021-02-22 00:07:50 +10:00
parent 426e09fb05
commit 986717a6ae
Signed by: lynnesbian
GPG key ID: F0A184B5213D9F90
4 changed files with 68 additions and 14 deletions

7
Cargo.lock generated
View file

@ -167,6 +167,12 @@ dependencies = [
"termcolor", "termcolor",
] ]
[[package]]
name = "exitcode"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
[[package]] [[package]]
name = "fif" name = "fif"
version = "0.2.2" version = "0.2.2"
@ -174,6 +180,7 @@ dependencies = [
"cached", "cached",
"clap", "clap",
"env_logger", "env_logger",
"exitcode",
"infer", "infer",
"log", "log",
"mime_guess", "mime_guess",

View file

@ -24,6 +24,7 @@ snailquote = "0.3.0"
once_cell = "1.5.2" once_cell = "1.5.2"
rayon = { version = "1.5.0", optional = true } rayon = { version = "1.5.0", optional = true }
infer = { version = "0.3.4", optional = true } infer = { version = "0.3.4", optional = true }
exitcode = "1.1.2"
# use git version while waiting on a release incorporating https://github.com/ebassi/xdg-mime-rs/commit/de5a6dd # use git version while waiting on a release incorporating https://github.com/ebassi/xdg-mime-rs/commit/de5a6dd
[target.'cfg(not(target_os = "windows"))'.dependencies] [target.'cfg(not(target_os = "windows"))'.dependencies]

View file

@ -18,7 +18,7 @@ use std::io::{stdout, BufWriter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use clap::Clap; use clap::Clap;
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn, error};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
#[cfg(feature = "multi-threaded")] #[cfg(feature = "multi-threaded")]
use rayon::prelude::*; use rayon::prelude::*;
@ -30,6 +30,7 @@ use crate::formats::{Format, Script};
use crate::mimedb::MimeDb; use crate::mimedb::MimeDb;
use crate::parameters::OutputFormat; use crate::parameters::OutputFormat;
use crate::scanerror::ScanError; use crate::scanerror::ScanError;
use std::process::exit;
mod findings; mod findings;
mod formats; mod formats;
@ -154,6 +155,7 @@ fn scan_from_walkdir(entries: Vec<DirEntry>) -> Vec<Result<Findings, (ScanError,
fn main() { fn main() {
let args = parameters::Parameters::parse(); let args = parameters::Parameters::parse();
let mut builder = env_logger::Builder::from_default_env(); let mut builder = env_logger::Builder::from_default_env();
builder builder
// .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args())) // .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args()))
@ -190,38 +192,81 @@ fn main() {
debug!("Checking files with extensions: {:?}", extensions); debug!("Checking files with extensions: {:?}", extensions);
let stepper = WalkDir::new(&args.dirs).into_iter(); let stepper = WalkDir::new(&args.dirs).into_iter();
let mut probably_fatal_error= false;
let entries: Vec<DirEntry> = stepper let entries: Vec<DirEntry> = stepper
.filter_entry(|e| wanted_file(&args, &extensions, e)) // filter out unwanted files .filter_entry(|e| wanted_file(&args, &extensions, e)) // filter out unwanted files
.filter_map(|e| e.ok()) // ignore anything that fails, e.g. files we don't have read access on .filter_map(|e| {
if let Err(err) = &e {
debug!("uh oh spaghettio!! {:#?}", e);
// log errors to stdout, and remove them from the iterator
let path = err
.path()
.map_or("General error".into(), Path::to_string_lossy);
if err.depth() == 0 {
// if something goes wrong while trying to read the root directory, we're probably not going to get much done
probably_fatal_error = true;
}
// TODO: is there a way to just say `map_or(x, |y| y).thing()` instead of `map_or(x.thing(), |y| y.thing())`?
// i don't care whether i'm returning a walkdir error or an io error, i just care about whether or not it
// implements ToString (which they both do). map_or doesn't work on trait objects though :(
error!("{}: {}", path, err.io_error().map_or(err.to_string(), |e|e.to_string()));
return None
}
e.ok()
})
.filter(|e| !e.file_type().is_dir()) // remove directories from the final list .filter(|e| !e.file_type().is_dir()) // remove directories from the final list
.collect(); .collect();
if entries.is_empty() {
if probably_fatal_error {
// no need to log anything for fatal errors - fif will already have printed something obvious like
// "[ERROR] /fake/path: No such file or directory (os error 2)". we can assume that if this has happened, the dir
// given as input doesn't exist or is otherwise unreadable.
exit(exitcode::NOINPUT);
}
warn!("No files matching requested options found.");
exit(exitcode::DATAERR);
}
trace!("Found {} items to check", entries.len()); trace!("Found {} items to check", entries.len());
let results = scan_from_walkdir(entries); let results: Vec<_> = scan_from_walkdir(entries)
.into_iter()
.filter(|result|
result.is_err()
|| !result.as_ref().unwrap().valid
// TODO: find a way to trace! the valid files without doing ↓
// || if result.as_ref().unwrap().valid { trace!("{:?} is fine", result.as_ref().unwrap().file); false } else { true }
)
.collect();
for result in &results { for result in &results {
match result { match result {
Ok(r) => { Ok(r) => {
if !r.valid { info!(
info!( "{:?} should have file extension {}",
"{:?} should have file extension {}", r.file,
r.file, r.recommended_extension().unwrap_or_else(|| "???".into())
r.recommended_extension().unwrap_or("???".into()) )
)
} else {
trace!("{:?} is totally fine", r.file)
}
} }
Err(f) => warn!("{:#?}: Error 0uo - {}", f.1, f.0), Err(f) => warn!("{:#?}: Error 0uo - {}", f.1, f.0),
} }
} }
if results.is_empty() { info!("All files have valid extensions!") }
match args.output_format { match args.output_format {
OutputFormat::Script => { OutputFormat::Script => {
let s = Script::new(); let s = Script::new();
s.write_all(&results, &mut BufWriter::new(stdout().lock())) if s.write_all(
.expect("failed to output"); &results,
&mut BufWriter::new(stdout().lock())
).is_err() {
exit(exitcode::IOERR);
}
} }
OutputFormat::Text => todo!(), OutputFormat::Text => todo!(),
} }

View file

@ -1,5 +1,6 @@
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
#[derive(Debug)]
pub enum ScanError { pub enum ScanError {
File, File,
Mime, Mime,