Repository: tbillington/kondo (language: rust)

Issue:
Feature request: Offer a "dry-run" / "no-to-all" / "report" feature
Kondo is really nice! But
Say I don't want to let Kondo delete anything, right now.
Say I just want to know *how much space would it free up if I told it to delete all*?
If I run Kondo and say "n" every time it says

> Projects cleaned: 0, Bytes deleted: 0.0B

Which is correct but not what I wanted to know.

I think the best way to implement this would be a --report-only that prints the names of all dirs it would delete if run with -a, then prints the total projects and bytes that would be deleted.


Discussion / hints:
Hey, thanks for creating this issue.

What information would you like from a report like this? Obviously total amount of bytes reclaimable, but would you want anything else? eg number of projects of different types, statistics, etc.
@tbillington , thanks, the use case I am envisioning is getting the information to decide "should I run this with -y"? so although those other kinds of information sound *interesting* I don't think they're useful. The two pieces of information I think are useful are:

- A list of all the paths to be deleted, and
- Total amount of bytes

This tells me "what is going to be deleted" "what do i get out of it". The list of paths is so I can review to see if there's anything in the list that REALLY shouldn't be deleted.

And if you are going to include those two I think "number of paths deleted" is also worth including.

Fix patch:
diff --git a/kondo/src/main.rs b/kondo/src/main.rs
index 43f08ee..b2f3ebf 100644
--- a/kondo/src/main.rs
+++ b/kondo/src/main.rs
@@ -57,6 +57,10 @@ struct Opt {
     /// If there is no input, defaults to yes
     #[arg(short, long)]
     default: bool,
+
+    /// Only list the directories that would be cleaned, without actually cleaning them.
+    #[arg(short = 'n', long, conflicts_with = "all")]
+    dry_run: bool,
 }
 
 fn prepare_directories(dirs: Vec<PathBuf>) -> Result<Vec<PathBuf>, Box<dyn Error>> {
@@ -223,8 +227,15 @@ fn interactive_prompt(
     quiet: u8,
     mut clean_all: bool,
     default: bool,
-) {
+    dry_run: bool,
+) -> (usize, u64) {
+    let mut total_projects = 0;
+    let mut total_bytes = 0;
+
     'project_loop: for (project, artifact_dirs, artifact_bytes, last_modified) in projects_recv {
+        total_projects += 1;
+        total_bytes += artifact_bytes;
+
         if quiet == 0 {
             println!(
                 "{} {} project {last_modified}",
@@ -238,6 +249,8 @@ fn interactive_prompt(
 
         let clean_project = if clean_all {
             true
+        } else if dry_run {
+            false
         } else {
             loop {
                 print!(
@@ -288,6 +301,8 @@ fn interactive_prompt(
             }
         }
     }
+
+    (total_projects, total_bytes)
 }
 
 fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
@@ -341,12 +356,13 @@ fn main() -> Result<(), Box<dyn Error>> {
 
     let delete_handle = std::thread::spawn(move || process_deletes(proj_delete_recv));
 
-    interactive_prompt(
+    let (total_projects, total_bytes) = interactive_prompt(
         proj_discover_recv,
         proj_delete_send,
         opt.quiet,
         opt.all,
         opt.default,
+        opt.dry_run,
     );
 
     let delete_results = match delete_handle.join() {
@@ -360,10 +376,13 @@ fn main() -> Result<(), Box<dyn Error>> {
     if opt.quiet < 2 {
         let projects_cleaned = delete_results.len();
         let bytes_deleted = delete_results.iter().map(|(_, bytes)| bytes).sum();
+
         println!(
-            "Projects cleaned: {}, Bytes deleted: {}",
+            "Projects cleaned: {}/{}, Bytes deleted: {} / {}",
             projects_cleaned,
-            pretty_size(bytes_deleted)
+            total_projects,
+            pretty_size(bytes_deleted),
+            pretty_size(total_bytes)
         );
     }
 


Test patch: