diff --git a/src/app.rs b/src/app.rs index cd6709f..c386265 100644 --- a/src/app.rs +++ b/src/app.rs @@ -124,6 +124,12 @@ pub fn build() -> App<'static, 'static> { .number_of_values(1) .help("How to display size"), ) + .arg( + Arg::with_name("total-size") + .long("total-size") + .multiple(true) + .help("Display the total size of directories"), + ) .arg( Arg::with_name("date") .long("date") diff --git a/src/core.rs b/src/core.rs index 8240ef8..74aaf89 100644 --- a/src/core.rs +++ b/src/core.rs @@ -116,6 +116,11 @@ impl Core { } }; } + if self.flags.total_size { + for meta in &mut meta_list.iter_mut() { + meta.calculate_total_size(); + } + } meta_list } diff --git a/src/flags.rs b/src/flags.rs index bb22a4e..790b554 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -17,6 +17,7 @@ pub struct Flags { pub recursion_depth: usize, pub blocks: Vec, pub no_symlink: bool, + pub total_size: bool, } impl Flags { @@ -91,6 +92,7 @@ impl Flags { None => usize::max_value(), }; let no_symlink = matches.is_present("no-symlink"); + let total_size = matches.is_present("total-size"); Ok(Self { display, @@ -125,6 +127,7 @@ impl Flags { DirOrderFlag::from(dir_order_inputs[dir_order_inputs.len() - 1]) }, no_symlink, + total_size, }) } } @@ -154,6 +157,7 @@ impl Default for Flags { Block::Name, ], no_symlink: false, + total_size: false, } } } diff --git a/src/meta/mod.rs b/src/meta/mod.rs index 6f77282..11bcdee 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -101,7 +101,7 @@ impl Meta { } } - let mut entry_meta = match Self::from_path(&path.to_path_buf()) { + let mut entry_meta = match Self::from_path(&path) { Ok(res) => res, Err(err) => { eprintln!("cannot access '{}': {}", path.display(), err); @@ -123,6 +123,66 @@ impl Meta { Ok(Some(content)) } + pub fn calculate_total_size(&mut self) { + if let FileType::Directory{ uid: _ } = self.file_type { + if let Some(metas) = &mut self.content { + let mut size_accumulated = self.size.get_bytes(); + for x in &mut metas.iter_mut() { + x.calculate_total_size(); + size_accumulated += x.size.get_bytes(); + } + self.size = Size::new(size_accumulated); + } else { // possibility that 'depth' limited the recursion in 'recurse_into' + self.size = Size::new(Meta::calculate_total_file_size(&self.path)); + } + } + } + + fn calculate_total_file_size(path: &PathBuf) -> u64 { + let metadata = if read_link(&path).is_ok() { + // If the file is a link, retrieve the metadata without following + // the link. + path.symlink_metadata() + } else { + path.metadata() + }; + let metadata = match metadata { + Ok(meta) => meta, + Err(err) => { + eprintln!("cannot access '{}': {}", path.display(), err); + return 0; + } + }; + let file_type = metadata.file_type(); + if file_type.is_file() { + metadata.len() + } else if file_type.is_dir() { + let mut size = metadata.len(); + + let entries = match path.read_dir() { + Ok(entries) => entries, + Err(err) => { + eprintln!("cannot access '{}': {}", path.display(), err); + return size; + } + }; + for entry in entries { + let path = match entry { + Ok(entry) => entry.path(), + Err(err) => { + eprintln!("cannot access '{}': {}", path.display(), err); + continue; + } + }; + size += Meta::calculate_total_file_size(&path); + } + size + } + else { + 0 + } + } + pub fn from_path(path: &PathBuf) -> Result { let metadata = if read_link(path).is_ok() { // If the file is a link, retrieve the metadata without following diff --git a/src/meta/size.rs b/src/meta/size.rs index e78e282..63ad688 100644 --- a/src/meta/size.rs +++ b/src/meta/size.rs @@ -25,6 +25,10 @@ impl<'a> From<&'a Metadata> for Size { } impl Size { + pub fn new(bytes: u64) -> Self { + Self { bytes: bytes } + } + pub fn get_bytes(&self) -> u64 { self.bytes }