Proper error handling within walk.rs

This commit is contained in:
sharkdp 2020-04-03 21:18:54 +02:00 committed by David Peter
parent bce95274e3
commit 4590ae8535
5 changed files with 38 additions and 48 deletions

View File

@ -1,10 +1,3 @@
macro_rules! print_error { pub fn print_error(msg: impl Into<String>) {
($($arg:tt)*) => (eprintln!("[fd error]: {}", format!($($arg)*))) eprintln!("[fd error]: {}", msg.into());
}
macro_rules! print_error_and_exit {
($($arg:tt)*) => {
print_error!($($arg)*);
::std::process::exit(1);
};
} }

View File

@ -3,6 +3,7 @@ use std::io::Write;
use std::process::Command; use std::process::Command;
use std::sync::Mutex; use std::sync::Mutex;
use crate::error::print_error;
use crate::exit_codes::ExitCode; use crate::exit_codes::ExitCode;
/// Executes a command. /// Executes a command.
@ -30,11 +31,11 @@ pub fn execute_command(mut cmd: Command, out_perm: &Mutex<()>) -> ExitCode {
} }
} }
Err(ref why) if why.kind() == io::ErrorKind::NotFound => { Err(ref why) if why.kind() == io::ErrorKind::NotFound => {
print_error!("Command not found: {:?}", cmd); print_error(format!("Command not found: {:?}", cmd));
ExitCode::GeneralError ExitCode::GeneralError
} }
Err(why) => { Err(why) => {
print_error!("Problem while executing command: {}", why); print_error(format!("Problem while executing command: {}", why));
ExitCode::GeneralError ExitCode::GeneralError
} }
} }

View File

@ -1,10 +1,13 @@
use super::CommandTemplate;
use crate::exit_codes::{merge_exitcodes, ExitCode};
use crate::walk::WorkerResult;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::error::print_error;
use crate::exit_codes::{merge_exitcodes, ExitCode};
use crate::walk::WorkerResult;
use super::CommandTemplate;
/// An event loop that listens for inputs from the `rx` receiver. Each received input will /// An event loop that listens for inputs from the `rx` receiver. Each received input will
/// generate a command with the supplied command template. The generated command will then /// generate a command with the supplied command template. The generated command will then
/// be executed, and this process will continue until the receiver's sender has closed. /// be executed, and this process will continue until the receiver's sender has closed.
@ -25,7 +28,7 @@ pub fn job(
Ok(WorkerResult::Entry(val)) => val, Ok(WorkerResult::Entry(val)) => val,
Ok(WorkerResult::Error(err)) => { Ok(WorkerResult::Error(err)) => {
if show_filesystem_errors { if show_filesystem_errors {
print_error!("{}", err); print_error(err.to_string());
} }
continue; continue;
} }
@ -50,7 +53,7 @@ pub fn batch(
WorkerResult::Entry(val) => Some(val), WorkerResult::Entry(val) => Some(val),
WorkerResult::Error(err) => { WorkerResult::Error(err) => {
if show_filesystem_errors { if show_filesystem_errors {
print_error!("{}", err); print_error(err.to_string());
} }
None None
} }

View File

@ -303,9 +303,7 @@ fn run() -> Result<ExitCode> {
) )
})?; })?;
let exit_code = walk::scan(&dir_vec, Arc::new(re), Arc::new(config)); walk::scan(&dir_vec, Arc::new(re), Arc::new(config))
Ok(exit_code)
} }
fn main() { fn main() {

View File

@ -1,9 +1,3 @@
use crate::exec;
use crate::exit_codes::{merge_exitcodes, ExitCode};
use crate::filesystem;
use crate::options::Options;
use crate::output;
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::{FileType, Metadata}; use std::fs::{FileType, Metadata};
@ -16,10 +10,18 @@ use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time; use std::time;
use anyhow::{anyhow, Result};
use ignore::overrides::OverrideBuilder; use ignore::overrides::OverrideBuilder;
use ignore::{self, WalkBuilder}; use ignore::{self, WalkBuilder};
use regex::bytes::Regex; use regex::bytes::Regex;
use crate::error::print_error;
use crate::exec;
use crate::exit_codes::{merge_exitcodes, ExitCode};
use crate::filesystem;
use crate::options::Options;
use crate::output;
/// The receiver thread can either be buffering results or directly streaming to the console. /// The receiver thread can either be buffering results or directly streaming to the console.
enum ReceiverMode { enum ReceiverMode {
/// Receiver is still buffering in order to sort the results, if the search finishes fast /// Receiver is still buffering in order to sort the results, if the search finishes fast
@ -44,7 +46,7 @@ pub const MAX_BUFFER_LENGTH: usize = 1000;
/// If the `--exec` argument was supplied, this will create a thread pool for executing /// If the `--exec` argument was supplied, this will create a thread pool for executing
/// jobs in parallel from a given command line and the discovered paths. Otherwise, each /// jobs in parallel from a given command line and the discovered paths. Otherwise, each
/// path will simply be written to standard output. /// path will simply be written to standard output.
pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) -> ExitCode { pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) -> Result<ExitCode> {
let mut path_iter = path_vec.iter(); let mut path_iter = path_vec.iter();
let first_path_buf = path_iter let first_path_buf = path_iter
.next() .next()
@ -54,14 +56,13 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) ->
let mut override_builder = OverrideBuilder::new(first_path_buf.as_path()); let mut override_builder = OverrideBuilder::new(first_path_buf.as_path());
for pattern in &config.exclude_patterns { for pattern in &config.exclude_patterns {
let res = override_builder.add(pattern); override_builder
if res.is_err() { .add(pattern)
print_error_and_exit!("Malformed exclude pattern '{}'", pattern); .map_err(|e| anyhow!("Malformed exclude pattern: {}", e))?;
}
} }
let overrides = override_builder.build().unwrap_or_else(|_| { let overrides = override_builder
print_error_and_exit!("Mismatch in exclude patterns"); .build()
}); .map_err(|_| anyhow!("Mismatch in exclude patterns"))?;
let mut walker = WalkBuilder::new(first_path_buf.as_path()); let mut walker = WalkBuilder::new(first_path_buf.as_path());
walker walker
@ -86,13 +87,10 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) ->
match result { match result {
Some(ignore::Error::Partial(_)) => (), Some(ignore::Error::Partial(_)) => (),
Some(err) => { Some(err) => {
print_error!( print_error(format!(
"{}", "Malformed pattern in custom ignore file. {}.",
format!( err.to_string()
"Malformed pattern in custom ignore file. {}.", ));
err.to_string()
)
);
} }
None => (), None => (),
} }
@ -131,7 +129,7 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<Options>) ->
process::exit(ExitCode::KilledBySigint.into()); process::exit(ExitCode::KilledBySigint.into());
} }
exit_code Ok(exit_code)
} }
fn spawn_receiver( fn spawn_receiver(
@ -231,7 +229,7 @@ fn spawn_receiver(
} }
WorkerResult::Error(err) => { WorkerResult::Error(err) => {
if show_filesystem_errors { if show_filesystem_errors {
print_error!("{}", err); print_error(err.to_string());
} }
} }
} }
@ -344,12 +342,9 @@ fn spawn_senders(
let entry_path = entry.path(); let entry_path = entry.path();
let search_str: Cow<OsStr> = if config.search_full_path { let search_str: Cow<OsStr> = if config.search_full_path {
match filesystem::path_absolute_form(entry_path) { let path_abs_buf = filesystem::path_absolute_form(entry_path)
Ok(path_abs_buf) => Cow::Owned(path_abs_buf.as_os_str().to_os_string()), .expect("Retrieving absolute path succeeds");
Err(_) => { Cow::Owned(path_abs_buf.as_os_str().to_os_string())
print_error_and_exit!("Unable to retrieve absolute path.");
}
}
} else { } else {
match entry_path.file_name() { match entry_path.file_name() {
Some(filename) => Cow::Borrowed(filename), Some(filename) => Cow::Borrowed(filename),