diff --git a/Cargo.lock b/Cargo.lock index 080ae12..a5ea2e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +[[package]] +name = "bytesize" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c8f83209414aacf0eeae3cf730b18d6981697fba62f200fcfb92b9f082acba" + [[package]] name = "cfg-if" version = "1.0.0" @@ -634,6 +640,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "bytesize", "clap", "env_logger", "goblin", diff --git a/Cargo.toml b/Cargo.toml index 53bc72a..3fe8e90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ nix = { version = "0.29", features = ["fs"] } petgraph = { version = "0.7" } tokio = { version = "1", features = ["full"] } walkdir = { version = "2" } +bytesize = { version = "2.0" } \ No newline at end of file diff --git a/src/cleaners.rs b/src/cleaners.rs index 3e2860a..d431716 100644 --- a/src/cleaners.rs +++ b/src/cleaners.rs @@ -10,10 +10,11 @@ use crate::{ }; use anyhow::{Error, Result}; use async_trait::async_trait; +use bytesize::ByteSize; use dso::DsoCleaner; use list::ListCleaner; use nix::libc::EXDEV; -use std::{collections::HashMap, io, path::Path}; +use std::{collections::HashMap, fmt, io, ops::AddAssign, path::Path}; use tokio::{sync::mpsc, task::JoinSet}; use walkdir::{DirEntry, WalkDir}; @@ -26,8 +27,22 @@ pub trait Cleaner { ) -> Result<()>; } +struct FileSize(u64); + +impl fmt::Display for FileSize { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", ByteSize(self.0)) + } +} + +impl AddAssign for FileSize { + fn add_assign(&mut self, rhs: Self) { + self.0.add_assign(rhs.0); + } +} + type Cleaners = Vec>; -type RemovalFn = Box io::Result<()>>; +type RemovalFn = Box io::Result>; pub struct Runner { cleaners: Cleaners, @@ -129,6 +144,8 @@ impl Runner { async fn final_decision(removal_fn: RemovalFn, mut output_rx: mpsc::Receiver) { let mut final_decisions = HashMap::new(); + let mut total_removed_size = FileSize(0); + while let Some(input_decision) = output_rx.recv().await { if input_decision.action == Action::Undecided { continue; @@ -147,28 +164,38 @@ impl Runner { for (file, action) in final_decisions { if action == Action::Remove { - if let Err(err) = (removal_fn)(&file) { - log::error!("{}: {}", file.display(), err); - } + let removed_size = match (removal_fn)(&file) { + Ok(size) => size, + Err(err) => { + log::error!("{}: {}", file.display(), err); + FileSize(0) + } + }; + total_removed_size += removed_size; } } + log::info!("Total space removed: {}", total_removed_size); } fn new_removal_fn(args: &Args) -> RemovalFn { if let Some(dest) = args.split_to.clone() { if args.dry_run { Box::new(move |path| { + let size = Self::get_file_size(path)?; log::info!( - "(dry-run) would move {} to {}", + "(dry-run) would move {} to {} ({})", path.display(), - dest.display() + dest.display(), + size ); - Ok(()) + Ok(size) }) } else { Box::new(move |path| { - log::info!("moving {} to {}", path.display(), dest.display()); - Self::move_preserve(&path, &dest) + let size = Self::get_file_size(path)?; + log::info!("moving {} to {} ({})", path.display(), dest.display(), size); + Self::move_preserve(&path, &dest)?; + Ok(size) }) } } else { @@ -179,18 +206,33 @@ impl Runner { } else { "regular file" }; - log::info!("(dry-run) would remove {} {}", ty, path.display()); - Ok(()) + let size = Self::get_file_size(path)?; + log::info!("(dry-run) would remove {} {} ({})", ty, path.display(), size); + Ok(size) }) } else { Box::new(move |path| { - log::info!("removing {}", path.display()); - std::fs::remove_file(&path) + let size = Self::get_file_size(path)?; + log::info!("removing {} ({})", path.display(), size); + std::fs::remove_file(&path)?; + Ok(size) }) } } } + fn get_file_size(file: &Path) -> io::Result { + let lstat = nix::sys::stat::lstat(file); + let size = match lstat { + Err(err) => { + log::error!("failed to get metadata from: {}, {}", file.display(), err); + FileSize(0) + }, + Ok(lstat) => FileSize(lstat.st_size as u64) + }; + Ok(size) + } + fn move_preserve(src: &Path, dest: &Path) -> io::Result<()> { assert!(src.is_relative()); let abs_dest = dest.join(src);