summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandy.boot <bootandy@gmail.com>2024-01-03 23:07:06 +0000
committerandy.boot <bootandy@gmail.com>2024-01-09 23:02:01 +0000
commite80892a9e7eb9e2c342328242cb51a4e1536b311 (patch)
tree4ac92da2db76addff9d06c28ffb7881ffd507c84
parentcd53fc74945010c06c6a6e0658a9c34e13f5a22c (diff)
feat: better error messages
Provide "No such file or directory" error if file is not found. Provide "Unknown Error" if other error found Should reduce confusion from the generic other error
-rw-r--r--src/dir_walker.rs123
-rw-r--r--src/main.rs31
-rw-r--r--src/progress.rs11
-rw-r--r--tests/test_flags.rs2
4 files changed, 114 insertions, 53 deletions
diff --git a/src/dir_walker.rs b/src/dir_walker.rs
index 2863751..120d56c 100644
--- a/src/dir_walker.rs
+++ b/src/dir_walker.rs
@@ -1,9 +1,11 @@
use std::fs;
use std::sync::Arc;
+use std::sync::Mutex;
use crate::node::Node;
use crate::progress::Operation;
use crate::progress::PAtomicInfo;
+use crate::progress::RuntimeErrors;
use crate::progress::ORDERING;
use crate::utils::is_filtered_out_due_to_invert_regex;
use crate::utils::is_filtered_out_due_to_regex;
@@ -28,16 +30,17 @@ pub struct WalkData<'a> {
pub ignore_hidden: bool,
pub follow_links: bool,
pub progress_data: Arc<PAtomicInfo>,
+ pub errors: Arc<Mutex<RuntimeErrors>>,
}
-pub fn walk_it(dirs: HashSet<PathBuf>, walk_data: WalkData) -> Vec<Node> {
+pub fn walk_it(dirs: HashSet<PathBuf>, walk_data: &WalkData) -> Vec<Node> {
let mut inodes = HashSet::new();
let top_level_nodes: Vec<_> = dirs
.into_iter()
.filter_map(|d| {
let prog_data = &walk_data.progress_data;
prog_data.clear_state(&d);
- let node = walk(d, &walk_data, 0)?;
+ let node = walk(d, walk_data, 0)?;
prog_data.state.store(Operation::PREPARING, ORDERING);
@@ -126,55 +129,83 @@ fn ignore_file(entry: &DirEntry, walk_data: &WalkData) -> bool {
fn walk(dir: PathBuf, walk_data: &WalkData, depth: usize) -> Option<Node> {
let prog_data = &walk_data.progress_data;
- let mut children = vec![];
-
- if let Ok(entries) = fs::read_dir(&dir) {
- children = entries
- .into_iter()
- .par_bridge()
- .filter_map(|entry| {
- if let Ok(ref entry) = entry {
- // uncommenting the below line gives simpler code but
- // rayon doesn't parallelize as well giving a 3X performance drop
- // hence we unravel the recursion a bit
-
- // return walk(entry.path(), walk_data, depth)
-
- if !ignore_file(entry, walk_data) {
- if let Ok(data) = entry.file_type() {
- if data.is_dir() || (walk_data.follow_links && data.is_symlink()) {
- return walk(entry.path(), walk_data, depth + 1);
- }
+ let errors = &walk_data.errors;
- let node = build_node(
- entry.path(),
- vec![],
- walk_data.filter_regex,
- walk_data.invert_filter_regex,
- walk_data.use_apparent_size,
- data.is_symlink(),
- data.is_file(),
- walk_data.by_filecount,
- depth,
- );
-
- prog_data.num_files.fetch_add(1, ORDERING);
- if let Some(ref file) = node {
- prog_data.total_file_size.fetch_add(file.size, ORDERING);
- }
+ let children = if dir.is_dir() {
+ let read_dir = fs::read_dir(&dir);
+ match read_dir {
+ Ok(entries) => {
+ entries
+ .into_iter()
+ .par_bridge()
+ .filter_map(|entry| {
+ if let Ok(ref entry) = entry {
+ // uncommenting the below line gives simpler code but
+ // rayon doesn't parallelize as well giving a 3X performance drop
+ // hence we unravel the recursion a bit
+
+ // return walk(entry.path(), walk_data, depth)
+
+ if !ignore_file(entry, walk_data) {
+ if let Ok(data) = entry.file_type() {
+ if data.is_dir()
+ || (walk_data.follow_links && data.is_symlink())
+ {
+ return walk(entry.path(), walk_data, depth + 1);
+ }
- return node;
+ let node = build_node(
+ entry.path(),
+ vec![],
+ walk_data.filter_regex,
+ walk_data.invert_filter_regex,
+ walk_data.use_apparent_size,
+ data.is_symlink(),
+ data.is_file(),
+ walk_data.by_filecount,
+ depth,
+ );
+
+ prog_data.num_files.fetch_add(1, ORDERING);
+ if let Some(ref file) = node {
+ prog_data.total_file_size.fetch_add(file.size, ORDERING);
+ }
+
+ return node;
+ }
+ }
+ } else {
+ let mut editable_error = errors.lock().unwrap();
+ editable_error.no_permissions = true
}
+ None
+ })
+ .collect()
+ }
+ Err(failed) => {
+ let mut editable_error = errors.lock().unwrap();
+ match failed.kind() {
+ std::io::ErrorKind::PermissionDenied => {
+ editable_error.no_permissions = true;
+ }
+ std::io::ErrorKind::NotFound => {
+ editable_error.file_not_found.insert(failed.to_string());
+ }
+ _ => {
+ editable_error.unknown_error.insert(failed.to_string());
}
- } else {
- prog_data.no_permissions.store(true, ORDERING)
}
- None
- })
- .collect();
- } else if !dir.is_file() {
- walk_data.progress_data.no_permissions.store(true, ORDERING)
- }
+ vec![]
+ }
+ }
+ } else {
+ if !dir.is_file() {
+ let mut editable_error = errors.lock().unwrap();
+ let bad_file = dir.as_os_str().to_string_lossy().into();
+ editable_error.file_not_found.insert(bad_file);
+ }
+ vec![]
+ };
build_node(
dir,
children,
diff --git a/src/main.rs b/src/main.rs
index 52c0a0c..0b81002 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,17 +11,19 @@ mod progress;
mod utils;
use crate::cli::build_cli;
+use crate::progress::RuntimeErrors;
use clap::parser::ValuesRef;
use dir_walker::WalkData;
use display::InitialDisplayData;
use filter::AggregateData;
use progress::PIndicator;
-use progress::ORDERING;
use regex::Error;
use std::collections::HashSet;
use std::fs::read_to_string;
use std::panic;
use std::process;
+use std::sync::Arc;
+use std::sync::Mutex;
use sysinfo::{System, SystemExt};
use self::display::draw_it;
@@ -195,11 +197,12 @@ fn main() {
ignore_hidden,
follow_links,
progress_data: indicator.data.clone(),
+ errors: Arc::new(Mutex::new(RuntimeErrors::default())),
};
let stack_size = config.get_custom_stack_size(&options);
init_rayon(&stack_size);
- let top_level_nodes = walk_it(simplified_dirs, walk_data);
+ let top_level_nodes = walk_it(simplified_dirs, &walk_data);
let tree = match summarize_file_types {
true => get_all_file_types(&top_level_nodes, number_of_lines),
@@ -216,12 +219,32 @@ fn main() {
}
};
- let failed_permissions = indicator.data.no_permissions.load(ORDERING);
- indicator.stop();
// Must have stopped indicator before we print to stderr
+ indicator.stop();
+
+ let final_errors = walk_data.errors.lock().unwrap();
+ let failed_permissions = final_errors.no_permissions;
+ if !final_errors.file_not_found.is_empty() {
+ let err = final_errors
+ .file_not_found
+ .iter()
+ .map(|a| a.as_ref())
+ .collect::<Vec<&str>>()
+ .join(", ");
+ eprintln!("No such file or directory: {}", err);
+ }
if failed_permissions {
eprintln!("Did not have permissions for all directories");
}
+ if !final_errors.unknown_error.is_empty() {
+ let err = final_errors
+ .unknown_error
+ .iter()
+ .map(|a| a.as_ref())
+ .collect::<Vec<&str>>()
+ .join(", ");
+ eprintln!("Unknown Error: {}", err);
+ }
if let Some(root_node) = tree {
let idd = InitialDisplayData {
diff --git a/src/progress.rs b/src/progress.rs
index 15f7722..b4d7a89 100644
--- a/src/progress.rs
+++ b/src/progress.rs
@@ -1,8 +1,9 @@
use std::{
+ collections::HashSet,
io::Write,
path::Path,
sync::{
- atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering},
+ atomic::{AtomicU64, AtomicU8, AtomicUsize, Ordering},
mpsc::{self, RecvTimeoutError, Sender},
Arc, RwLock,
},
@@ -55,7 +56,6 @@ pub struct PAtomicInfo {
pub total_file_size: AtomicU64,
pub state: AtomicU8,
pub current_path: ThreadStringWrapper,
- pub no_permissions: AtomicBool,
}
impl PAtomicInfo {
@@ -68,6 +68,13 @@ impl PAtomicInfo {
}
}
+#[derive(Default)]
+pub struct RuntimeErrors {
+ pub no_permissions: bool,
+ pub file_not_found: HashSet<String>,
+ pub unknown_error: HashSet<String>,
+}
+
/* -------------------------------------------------------------------------- */
fn format_preparing_str(prog_char: char, data: &PAtomicInfo, is_iso: bool) -> String {
diff --git a/tests/test_flags.rs b/tests/test_flags.rs
index 199ac0e..3251692 100644
--- a/tests/test_flags.rs
+++ b/tests/test_flags.rs
@@ -92,7 +92,7 @@ pub fn test_with_bad_param() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let result = cmd.arg("bad_place").unwrap();
let stderr = str::from_utf8(&result.stderr).unwrap();
- assert!(stderr.contains("Did not have permissions for all directories"));
+ assert!(stderr.contains("No such file or directory"));
}
#[test]