summaryrefslogtreecommitdiff
path: root/cli/tools/clean.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/clean.rs')
-rw-r--r--cli/tools/clean.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs
new file mode 100644
index 000000000..2a77434f8
--- /dev/null
+++ b/cli/tools/clean.rs
@@ -0,0 +1,97 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use std::path::Path;
+
+use crate::cache::DenoDir;
+use crate::colors;
+use crate::display;
+use crate::util::progress_bar::ProgressBar;
+use crate::util::progress_bar::ProgressBarStyle;
+use crate::util::progress_bar::ProgressMessagePrompt;
+use crate::util::progress_bar::UpdateGuard;
+
+struct CleanState {
+ files_removed: u64,
+ dirs_removed: u64,
+ bytes_removed: u64,
+ progress_guard: UpdateGuard,
+}
+
+impl CleanState {
+ fn update_progress(&self) {
+ self
+ .progress_guard
+ .set_position(self.files_removed + self.dirs_removed);
+ }
+}
+
+pub fn clean() -> Result<(), AnyError> {
+ let deno_dir = DenoDir::new(None)?;
+ if deno_dir.root.exists() {
+ let no_of_files = walkdir::WalkDir::new(&deno_dir.root).into_iter().count();
+ let progress_bar = ProgressBar::new(ProgressBarStyle::ProgressBars);
+ let progress_guard =
+ progress_bar.update_with_prompt(ProgressMessagePrompt::Cleaning, "");
+
+ let mut state = CleanState {
+ files_removed: 0,
+ dirs_removed: 0,
+ bytes_removed: 0,
+ progress_guard,
+ };
+ state
+ .progress_guard
+ .set_total_size(no_of_files.try_into().unwrap());
+
+ rm_rf(&mut state, &deno_dir.root)?;
+
+ // Drop the guard so that progress bar disappears.
+ drop(state.progress_guard);
+
+ log::info!(
+ "{} {} {}",
+ colors::green("Removed"),
+ deno_dir.root.display(),
+ colors::gray(&format!(
+ "({} files, {})",
+ state.files_removed + state.dirs_removed,
+ display::human_size(state.bytes_removed as f64)
+ ))
+ );
+ }
+
+ Ok(())
+}
+
+fn rm_rf(state: &mut CleanState, path: &Path) -> Result<(), AnyError> {
+ for entry in walkdir::WalkDir::new(path).contents_first(true) {
+ let entry = entry?;
+
+ if entry.file_type().is_dir() {
+ state.dirs_removed += 1;
+ state.update_progress();
+ std::fs::remove_dir_all(entry.path())?;
+ } else {
+ remove_file(state, entry.path(), entry.metadata().ok())?;
+ }
+ }
+
+ Ok(())
+}
+
+fn remove_file(
+ state: &mut CleanState,
+ path: &Path,
+ meta: Option<std::fs::Metadata>,
+) -> Result<(), AnyError> {
+ if let Some(meta) = meta {
+ state.bytes_removed += meta.len();
+ }
+ state.files_removed += 1;
+ state.update_progress();
+ std::fs::remove_file(path)
+ .with_context(|| format!("Failed to remove file: {}", path.display()))?;
+ Ok(())
+}