nicer error handling, exit codes
This commit is contained in:
parent
426e09fb05
commit
986717a6ae
4 changed files with 68 additions and 14 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -167,6 +167,12 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exitcode"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
||||
|
||||
[[package]]
|
||||
name = "fif"
|
||||
version = "0.2.2"
|
||||
|
@ -174,6 +180,7 @@ dependencies = [
|
|||
"cached",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"exitcode",
|
||||
"infer",
|
||||
"log",
|
||||
"mime_guess",
|
||||
|
|
|
@ -24,6 +24,7 @@ snailquote = "0.3.0"
|
|||
once_cell = "1.5.2"
|
||||
rayon = { version = "1.5.0", 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
|
||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||
|
|
65
src/main.rs
65
src/main.rs
|
@ -18,7 +18,7 @@ use std::io::{stdout, BufWriter};
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use clap::Clap;
|
||||
use log::{debug, info, trace, warn};
|
||||
use log::{debug, info, trace, warn, error};
|
||||
use once_cell::sync::OnceCell;
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
use rayon::prelude::*;
|
||||
|
@ -30,6 +30,7 @@ use crate::formats::{Format, Script};
|
|||
use crate::mimedb::MimeDb;
|
||||
use crate::parameters::OutputFormat;
|
||||
use crate::scanerror::ScanError;
|
||||
use std::process::exit;
|
||||
|
||||
mod findings;
|
||||
mod formats;
|
||||
|
@ -154,6 +155,7 @@ fn scan_from_walkdir(entries: Vec<DirEntry>) -> Vec<Result<Findings, (ScanError,
|
|||
|
||||
fn main() {
|
||||
let args = parameters::Parameters::parse();
|
||||
|
||||
let mut builder = env_logger::Builder::from_default_env();
|
||||
builder
|
||||
// .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args()))
|
||||
|
@ -190,38 +192,81 @@ fn main() {
|
|||
debug!("Checking files with extensions: {:?}", extensions);
|
||||
|
||||
let stepper = WalkDir::new(&args.dirs).into_iter();
|
||||
let mut probably_fatal_error= false;
|
||||
let entries: Vec<DirEntry> = stepper
|
||||
.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
|
||||
.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());
|
||||
|
||||
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 {
|
||||
match result {
|
||||
Ok(r) => {
|
||||
if !r.valid {
|
||||
info!(
|
||||
"{:?} should have file extension {}",
|
||||
r.file,
|
||||
r.recommended_extension().unwrap_or("???".into())
|
||||
r.recommended_extension().unwrap_or_else(|| "???".into())
|
||||
)
|
||||
} else {
|
||||
trace!("{:?} is totally fine", r.file)
|
||||
}
|
||||
}
|
||||
Err(f) => warn!("{:#?}: Error 0uo - {}", f.1, f.0),
|
||||
}
|
||||
}
|
||||
|
||||
if results.is_empty() { info!("All files have valid extensions!") }
|
||||
|
||||
match args.output_format {
|
||||
OutputFormat::Script => {
|
||||
let s = Script::new();
|
||||
s.write_all(&results, &mut BufWriter::new(stdout().lock()))
|
||||
.expect("failed to output");
|
||||
if s.write_all(
|
||||
&results,
|
||||
&mut BufWriter::new(stdout().lock())
|
||||
).is_err() {
|
||||
exit(exitcode::IOERR);
|
||||
}
|
||||
}
|
||||
OutputFormat::Text => todo!(),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScanError {
|
||||
File,
|
||||
Mime,
|
||||
|
|
Loading…
Reference in a new issue