87
src/main.rs
87
src/main.rs
@@ -1,41 +1,82 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc, Mutex,
|
||||||
atomic::{AtomicU64, AtomicUsize, Ordering},
|
atomic::{AtomicU64, AtomicUsize, Ordering},
|
||||||
},
|
},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::{Parser, ValueEnum};
|
||||||
use crossbeam_channel as chan;
|
use crossbeam_channel as chan;
|
||||||
use humansize::DECIMAL;
|
use humansize::DECIMAL;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, ValueEnum)]
|
||||||
|
enum SortOrder {
|
||||||
|
Increasing,
|
||||||
|
Decreasing,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, ValueEnum)]
|
||||||
|
enum SortMode {
|
||||||
|
Path,
|
||||||
|
Size,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
#[arg(short = 'j', long = "jobs")]
|
#[arg(short = 'j', long = "jobs")]
|
||||||
jobs: Option<usize>,
|
jobs: Option<usize>,
|
||||||
|
#[arg(short = 'd', long = "dir-sizes")]
|
||||||
|
dir_sizes: bool,
|
||||||
|
#[arg(short, long)]
|
||||||
|
bytes: bool,
|
||||||
|
#[arg(short = 'O', long, value_enum, default_value_t = SortOrder::Increasing)]
|
||||||
|
sort_order: SortOrder,
|
||||||
|
#[arg(short = 'M', long, value_enum, default_value_t = SortMode::Path)]
|
||||||
|
sort_mode: SortMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let jobs = args.jobs.unwrap_or_else(num_cpus::get).max(1);
|
let jobs = args.jobs.unwrap_or_else(num_cpus::get).max(1);
|
||||||
|
let root = args.path.clone();
|
||||||
|
|
||||||
let (tx, rx) = chan::unbounded::<PathBuf>();
|
let (tx, rx) = chan::unbounded::<PathBuf>();
|
||||||
|
|
||||||
let pending = Arc::new(AtomicUsize::new(1));
|
let pending = Arc::new(AtomicUsize::new(1));
|
||||||
let total = Arc::new(AtomicU64::new(0));
|
let total = Arc::new(AtomicU64::new(0));
|
||||||
|
let dir_sizes = if args.dir_sizes {
|
||||||
|
Some(Arc::new(Mutex::new(HashMap::new())))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut workers = Vec::with_capacity(jobs);
|
let mut workers = Vec::with_capacity(jobs);
|
||||||
for _ in 0..jobs {
|
for _ in 0..jobs {
|
||||||
let rx = rx.clone();
|
let rx = rx.clone();
|
||||||
let total = total.clone();
|
let total = total.clone();
|
||||||
|
let dir_sizes = dir_sizes.clone();
|
||||||
|
let root = root.clone();
|
||||||
workers.push(thread::spawn(move || {
|
workers.push(thread::spawn(move || {
|
||||||
while let Ok(path) = rx.recv() {
|
while let Ok(path) = rx.recv() {
|
||||||
if let Ok(meta) = std::fs::metadata(&path) {
|
if let Ok(meta) = std::fs::metadata(&path) {
|
||||||
if meta.is_file() {
|
if meta.is_file() {
|
||||||
total.fetch_add(meta.len(), Ordering::Relaxed);
|
let size = meta.len();
|
||||||
|
total.fetch_add(size, Ordering::Relaxed);
|
||||||
|
if let Some(dir_sizes) = dir_sizes.as_ref() {
|
||||||
|
let mut map = dir_sizes.lock().unwrap();
|
||||||
|
let mut current = path.parent();
|
||||||
|
while let Some(dir) = current {
|
||||||
|
if !dir.starts_with(&root) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*map.entry(dir.to_path_buf()).or_insert(0) += size;
|
||||||
|
current = dir.parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,10 +139,40 @@ fn main() {
|
|||||||
let _ = w.join();
|
let _ = w.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(dir_sizes) = dir_sizes {
|
||||||
|
let mut entries: Vec<(PathBuf, u64)> = dir_sizes
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|(path, size)| (path.clone(), *size))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
entries.sort_by(|(lp, li), (rp, ri)| {
|
||||||
|
let ord = match args.sort_mode {
|
||||||
|
SortMode::Path => lp.cmp(rp),
|
||||||
|
SortMode::Size => li.cmp(&ri),
|
||||||
|
};
|
||||||
|
|
||||||
|
match args.sort_order {
|
||||||
|
SortOrder::Increasing => ord,
|
||||||
|
SortOrder::Decreasing => ord.reverse(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (path, size) in entries {
|
||||||
|
let size = if args.bytes {
|
||||||
|
size.to_string()
|
||||||
|
} else {
|
||||||
|
humansize::format_size(size, DECIMAL)
|
||||||
|
};
|
||||||
|
println!("{}: {}", size, path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let size = total.load(Ordering::Relaxed);
|
let size = total.load(Ordering::Relaxed);
|
||||||
println!(
|
let size = if args.bytes {
|
||||||
"Computed size sum: {} ({} bytes)",
|
size.to_string()
|
||||||
humansize::format_size(size, DECIMAL),
|
} else {
|
||||||
size
|
humansize::format_size(size, DECIMAL)
|
||||||
);
|
};
|
||||||
|
println!("Total: {}", size);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user