From ac3ad430f1a6a6006876d18a2fdfff720b255ba5 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 21 Sep 2019 14:26:38 +0200 Subject: Add '-b'/'--apparent-size' option This commit changes `diskus` default behavior: we now report "disk usage" instead of "apparent size" in analogy to "du -sh". At the same time however, we introduce a new `--apparent-size` option which can be used to switch back to "apparent size". closes #25 --- src/filesize.rs | 23 +++++++++++++++++++++++ src/lib.rs | 8 +++++--- src/main.rs | 24 +++++++++++++++++++----- src/walk.rs | 17 ++++++++++++----- tests/walk.rs | 4 ++-- 5 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 src/filesize.rs diff --git a/src/filesize.rs b/src/filesize.rs new file mode 100644 index 0000000..0ac4c7e --- /dev/null +++ b/src/filesize.rs @@ -0,0 +1,23 @@ +#[derive(Debug, Clone, Copy)] +pub enum FilesizeType { + DiskUsage, + ApparentSize, +} + +impl FilesizeType { + #[cfg(not(windows))] + pub fn size(self, metadata: &std::fs::Metadata) -> u64 { + use std::os::unix::fs::MetadataExt; + + match self { + Self::ApparentSize => metadata.len(), + // block size is always 512 byte, see stat(2) manpage + Self::DiskUsage => metadata.blocks() * 512, + } + } + + #[cfg(windows)] + pub fn size(self, metadata: &std::fs::Metadata) -> u64 { + metadata.len() + } +} diff --git a/src/lib.rs b/src/lib.rs index 0867b2b..5f2b447 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,17 @@ //! //! ``` //! use std::path::PathBuf; -//! use diskus::Walk; +//! use diskus::{Walk, FilesizeType}; //! //! let num_threads = 4; //! let root_directories = &[PathBuf::from(".")]; -//! let walk = Walk::new(root_directories, num_threads); +//! let walk = Walk::new(root_directories, num_threads, FilesizeType::DiskUsage); //! let (size_in_bytes, errors) = walk.run(); //! ``` +mod filesize; mod unique_id; pub mod walk; -pub use crate::walk::Walk; +pub use crate::filesize::FilesizeType; +pub use crate::walk::{Error, Walk}; diff --git a/src/main.rs b/src/main.rs index 57f7c9e..693003d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,19 +5,19 @@ use humansize::file_size_opts::{self, FileSizeOpts}; use humansize::FileSize; use num_format::{Locale, ToFormattedString}; -use diskus::walk::{self, Walk}; +use diskus::{Error, FilesizeType, Walk}; -fn print_result(size: u64, errors: &[walk::Error], size_format: &FileSizeOpts, verbose: bool) { +fn print_result(size: u64, errors: &[Error], size_format: &FileSizeOpts, verbose: bool) { if verbose { for err in errors { match err { - walk::Error::NoMetadataForPath(path) => { + Error::NoMetadataForPath(path) => { eprintln!( "diskus: could not retrieve metadata for path '{}'", path.to_string_lossy() ); } - walk::Error::CouldNotReadDir(path) => { + Error::CouldNotReadDir(path) => { eprintln!( "diskus: could not read contents of directory '{}'", path.to_string_lossy() @@ -80,6 +80,14 @@ fn main() { .help("Do not hide filesystem errors"), ); + #[cfg(not(windows))] + let app = app.arg( + Arg::with_name("apparent-size") + .long("apparent-size") + .short("b") + .help("Compute apparent size instead of disk usage"), + ); + let matches = app.get_matches(); // Setting the number of threads to 3x the number of cores is a good tradeoff between @@ -97,6 +105,12 @@ fn main() { .map(|paths| paths.map(PathBuf::from).collect()) .unwrap_or_else(|| vec![PathBuf::from(".")]); + let filesize_type = if matches.is_present("apparent-size") { + FilesizeType::ApparentSize + } else { + FilesizeType::DiskUsage + }; + let size_format = match matches.value_of("size-format") { Some("decimal") => file_size_opts::DECIMAL, _ => file_size_opts::BINARY, @@ -104,7 +118,7 @@ fn main() { let verbose = matches.is_present("verbose"); - let walk = Walk::new(&paths, num_threads); + let walk = Walk::new(&paths, num_threads, filesize_type); let (size, errors) = walk.run(); print_result(size, &errors, &size_format, verbose); } diff --git a/src/walk.rs b/src/walk.rs index e982549..64ea982 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -7,6 +7,7 @@ use crossbeam_channel as channel; use rayon::{self, prelude::*}; +use crate::filesize::FilesizeType; use crate::unique_id::{generate_unique_id, UniqueID}; pub enum Error { @@ -19,12 +20,12 @@ enum Message { Error { error: Error }, } -fn walk(tx: channel::Sender, entries: &[PathBuf]) { +fn walk(tx: channel::Sender, entries: &[PathBuf], filesize_type: FilesizeType) { entries.into_par_iter().for_each_with(tx, |tx_ref, entry| { if let Ok(metadata) = entry.symlink_metadata() { let unique_id = generate_unique_id(&metadata); - let size = metadata.len(); + let size = filesize_type.size(&metadata); tx_ref.send(Message::SizeEntry(unique_id, size)).unwrap(); @@ -47,7 +48,7 @@ fn walk(tx: channel::Sender, entries: &[PathBuf]) { } } - walk(tx_ref.clone(), &children[..]); + walk(tx_ref.clone(), &children[..], filesize_type); }; } else { tx_ref @@ -62,13 +63,19 @@ fn walk(tx: channel::Sender, entries: &[PathBuf]) { pub struct Walk<'a> { root_directories: &'a [PathBuf], num_threads: usize, + filesize_type: FilesizeType, } impl<'a> Walk<'a> { - pub fn new(root_directories: &'a [PathBuf], num_threads: usize) -> Walk { + pub fn new( + root_directories: &'a [PathBuf], + num_threads: usize, + filesize_type: FilesizeType, + ) -> Walk { Walk { root_directories, num_threads, + filesize_type, } } @@ -103,7 +110,7 @@ impl<'a> Walk<'a> { .num_threads(self.num_threads) .build() .unwrap(); - pool.install(|| walk(tx, self.root_directories)); + pool.install(|| walk(tx, self.root_directories, self.filesize_type)); receiver_thread.join().unwrap() } diff --git a/tests/walk.rs b/tests/walk.rs index 5f2d3d2..6e1dd35 100644 --- a/tests/walk.rs +++ b/tests/walk.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use tempdir::TempDir; -use diskus::Walk; +use diskus::{FilesizeType, Walk}; #[test] fn size_of_single_file() -> Result<(), Box> { @@ -16,7 +16,7 @@ fn size_of_single_file() -> Result<(), Box> { let num_threads = 1; let root_directories = &[PathBuf::from(file_path)]; - let walk = Walk::new(root_directories, num_threads); + let walk = Walk::new(root_directories, num_threads, FilesizeType::ApparentSize); let (size_in_bytes, errors) = walk.run(); assert!(errors.is_empty()); -- cgit v1.2.3