Skip to main content

dry4rust/cli/
cleanup_command.rs

1// Copyright (c) 2026 Matjaz Domen Pecan
2// Copyright 2026 Umberto Gotti <umberto.gotti@umbertogotti.dev>
3// Licensed under the MIT License
4// SPDX-License-Identifier: MIT
5
6use std::io::Write;
7use std::path::Path;
8
9use crate::analysis::AnalysisResult;
10use crate::cli::checking::stale_report::StaleReport;
11use crate::cli::cli_error::CliResult;
12use crate::cli::ignore_entry_line::IgnoreEntryLine;
13use crate::suppression::ignore_file::IgnoreFile;
14
15/// `cleanup`: drop ignore entries whose fingerprint no longer matches anything.
16///
17/// In dry run the file is read and reported on but never written, so the
18/// command can be pointed at a repository whose ignore file is under review.
19pub struct CleanupCommand<'a> {
20    root: &'a Path,
21    result: &'a AnalysisResult,
22    dry_run: bool,
23}
24
25impl<'a> CleanupCommand<'a> {
26    #[must_use]
27    pub const fn new(root: &'a Path, result: &'a AnalysisResult, dry_run: bool) -> Self {
28        Self {
29            root,
30            result,
31            dry_run,
32        }
33    }
34
35    /// Report the stale entries, and remove them unless this is a dry run.
36    ///
37    /// # Errors
38    ///
39    /// Returns [`crate::cli::cli_error::CliError::Io`] if the ignore file
40    /// cannot be written or the writer fails.
41    pub fn run(&self, writer: &mut impl Write) -> CliResult {
42        let ignore_file = IgnoreFile::load(self.root);
43        let taken;
44
45        let report = if self.dry_run {
46            StaleReport::dry_run(ignore_file.stale(&self.result.all_fingerprints))
47        } else {
48            let pruned;
49            (pruned, taken) = ignore_file.without_stale(&self.result.all_fingerprints);
50            if !taken.is_empty() {
51                pruned.save(self.root)?;
52            }
53            StaleReport::removed(taken.iter().collect())
54        };
55
56        if report.is_empty() {
57            writeln!(writer, "No stale entries found.")?;
58            return Ok(());
59        }
60
61        writeln!(writer, "{}", report.heading())?;
62        for entry in report.entries() {
63            writeln!(writer, "{}", IgnoreEntryLine::new(entry))?;
64        }
65        writeln!(writer, "{}", report.summary())?;
66        Ok(())
67    }
68}