mirror of
https://github.com/bootandy/dust.git
synced 2026-06-08 11:29:05 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fead40b628 | |||
| 81dde7005e |
Generated
+1
-1
@@ -254,7 +254,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "du-dust"
|
name = "du-dust"
|
||||||
version = "0.8.6"
|
version = "0.9.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "du-dust"
|
name = "du-dust"
|
||||||
description = "A more intuitive version of du"
|
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>"]
|
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
.ie \n(.g .ds Aq \(aq
|
.ie \n(.g .ds Aq \(aq
|
||||||
.el .ds Aq '
|
.el .ds Aq '
|
||||||
.TH Dust 1 "Dust 0.8.6"
|
.TH Dust 1 "Dust 0.9.0"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
Dust \- Like du but more intuitive
|
Dust \- Like du but more intuitive
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -96,4 +96,4 @@ Print version
|
|||||||
[\fIparams\fR]
|
[\fIparams\fR]
|
||||||
|
|
||||||
.SH VERSION
|
.SH VERSION
|
||||||
v0.8.6
|
v0.9.0
|
||||||
|
|||||||
+75
-44
@@ -1,9 +1,11 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
use crate::progress::Operation;
|
use crate::progress::Operation;
|
||||||
use crate::progress::PAtomicInfo;
|
use crate::progress::PAtomicInfo;
|
||||||
|
use crate::progress::RuntimeErrors;
|
||||||
use crate::progress::ORDERING;
|
use crate::progress::ORDERING;
|
||||||
use crate::utils::is_filtered_out_due_to_invert_regex;
|
use crate::utils::is_filtered_out_due_to_invert_regex;
|
||||||
use crate::utils::is_filtered_out_due_to_regex;
|
use crate::utils::is_filtered_out_due_to_regex;
|
||||||
@@ -28,16 +30,17 @@ pub struct WalkData<'a> {
|
|||||||
pub ignore_hidden: bool,
|
pub ignore_hidden: bool,
|
||||||
pub follow_links: bool,
|
pub follow_links: bool,
|
||||||
pub progress_data: Arc<PAtomicInfo>,
|
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 mut inodes = HashSet::new();
|
||||||
let top_level_nodes: Vec<_> = dirs
|
let top_level_nodes: Vec<_> = dirs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|d| {
|
.filter_map(|d| {
|
||||||
let prog_data = &walk_data.progress_data;
|
let prog_data = &walk_data.progress_data;
|
||||||
prog_data.clear_state(&d);
|
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);
|
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> {
|
fn walk(dir: PathBuf, walk_data: &WalkData, depth: usize) -> Option<Node> {
|
||||||
let prog_data = &walk_data.progress_data;
|
let prog_data = &walk_data.progress_data;
|
||||||
let mut children = vec![];
|
let errors = &walk_data.errors;
|
||||||
|
|
||||||
if let Ok(entries) = fs::read_dir(&dir) {
|
let children = if dir.is_dir() {
|
||||||
children = entries
|
let read_dir = fs::read_dir(&dir);
|
||||||
.into_iter()
|
match read_dir {
|
||||||
.par_bridge()
|
Ok(entries) => {
|
||||||
.filter_map(|entry| {
|
entries
|
||||||
if let Ok(ref entry) = entry {
|
.into_iter()
|
||||||
// uncommenting the below line gives simpler code but
|
.par_bridge()
|
||||||
// rayon doesn't parallelize as well giving a 3X performance drop
|
.filter_map(|entry| {
|
||||||
// hence we unravel the recursion a bit
|
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 !ignore_file(entry, walk_data) {
|
||||||
if let Ok(data) = entry.file_type() {
|
if let Ok(data) = entry.file_type() {
|
||||||
if data.is_dir() || (walk_data.follow_links && data.is_symlink()) {
|
if data.is_dir()
|
||||||
return walk(entry.path(), walk_data, depth + 1);
|
|| (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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
let node = build_node(
|
let mut editable_error = errors.lock().unwrap();
|
||||||
entry.path(),
|
editable_error.no_permissions = true
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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
|
vec![]
|
||||||
})
|
}
|
||||||
.collect();
|
}
|
||||||
} else if !dir.is_file() {
|
} else {
|
||||||
walk_data.progress_data.no_permissions.store(true, ORDERING)
|
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(
|
build_node(
|
||||||
dir,
|
dir,
|
||||||
children,
|
children,
|
||||||
|
|||||||
+27
-4
@@ -11,17 +11,19 @@ mod progress;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use crate::cli::build_cli;
|
use crate::cli::build_cli;
|
||||||
|
use crate::progress::RuntimeErrors;
|
||||||
use clap::parser::ValuesRef;
|
use clap::parser::ValuesRef;
|
||||||
use dir_walker::WalkData;
|
use dir_walker::WalkData;
|
||||||
use display::InitialDisplayData;
|
use display::InitialDisplayData;
|
||||||
use filter::AggregateData;
|
use filter::AggregateData;
|
||||||
use progress::PIndicator;
|
use progress::PIndicator;
|
||||||
use progress::ORDERING;
|
|
||||||
use regex::Error;
|
use regex::Error;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
use sysinfo::{System, SystemExt};
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
use self::display::draw_it;
|
use self::display::draw_it;
|
||||||
@@ -195,11 +197,12 @@ fn main() {
|
|||||||
ignore_hidden,
|
ignore_hidden,
|
||||||
follow_links,
|
follow_links,
|
||||||
progress_data: indicator.data.clone(),
|
progress_data: indicator.data.clone(),
|
||||||
|
errors: Arc::new(Mutex::new(RuntimeErrors::default())),
|
||||||
};
|
};
|
||||||
let stack_size = config.get_custom_stack_size(&options);
|
let stack_size = config.get_custom_stack_size(&options);
|
||||||
init_rayon(&stack_size);
|
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 {
|
let tree = match summarize_file_types {
|
||||||
true => get_all_file_types(&top_level_nodes, number_of_lines),
|
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
|
// 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 {
|
if failed_permissions {
|
||||||
eprintln!("Did not have permissions for all directories");
|
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 {
|
if let Some(root_node) = tree {
|
||||||
let idd = InitialDisplayData {
|
let idd = InitialDisplayData {
|
||||||
|
|||||||
+9
-2
@@ -1,8 +1,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
io::Write,
|
io::Write,
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering},
|
atomic::{AtomicU64, AtomicU8, AtomicUsize, Ordering},
|
||||||
mpsc::{self, RecvTimeoutError, Sender},
|
mpsc::{self, RecvTimeoutError, Sender},
|
||||||
Arc, RwLock,
|
Arc, RwLock,
|
||||||
},
|
},
|
||||||
@@ -55,7 +56,6 @@ pub struct PAtomicInfo {
|
|||||||
pub total_file_size: AtomicU64,
|
pub total_file_size: AtomicU64,
|
||||||
pub state: AtomicU8,
|
pub state: AtomicU8,
|
||||||
pub current_path: ThreadStringWrapper,
|
pub current_path: ThreadStringWrapper,
|
||||||
pub no_permissions: AtomicBool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PAtomicInfo {
|
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 {
|
fn format_preparing_str(prog_char: char, data: &PAtomicInfo, is_iso: bool) -> String {
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ pub fn test_with_bad_param() {
|
|||||||
let mut cmd = Command::cargo_bin("dust").unwrap();
|
let mut cmd = Command::cargo_bin("dust").unwrap();
|
||||||
let result = cmd.arg("bad_place").unwrap();
|
let result = cmd.arg("bad_place").unwrap();
|
||||||
let stderr = str::from_utf8(&result.stderr).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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user