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",
|
"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",
|
||||||
|
|
|
@ -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]
|
||||||
|
|
73
src/main.rs
73
src/main.rs
|
@ -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!(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue