mirror of
https://github.com/bootandy/dust.git
synced 2025-12-06 12:51:10 -08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fead40b628 | ||
|
|
81dde7005e |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -254,7 +254,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "du-dust"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"assert_cmd",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "du-dust"
|
||||
description = "A more intuitive version of du"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH Dust 1 "Dust 0.8.6"
|
||||
.TH Dust 1 "Dust 0.9.0"
|
||||
.SH NAME
|
||||
Dust \- Like du but more intuitive
|
||||
.SH SYNOPSIS
|
||||
@@ -96,4 +96,4 @@ Print version
|
||||
[\fIparams\fR]
|
||||
|
||||
.SH VERSION
|
||||
v0.8.6
|
||||
v0.9.0
|
||||
|
||||
@@ -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![];
|
||||
let errors = &walk_data.errors;
|
||||
|
||||
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
|
||||
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)
|
||||
// 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);
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
31
src/main.rs
31
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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user