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::fmt::Write as _;
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::command_outcome::CommandOutcome;
13use crate::cli::ignore_entry_line::IgnoreEntryLine;
14use crate::suppression::ignore_file::IgnoreFile;
15
16/// `cleanup`: drop ignore entries whose fingerprint no longer matches anything.
17///
18/// In dry run the file is read and reported on but never written, so the
19/// command can be pointed at a repository whose ignore file is under review.
20pub struct CleanupCommand<'a> {
21    root: &'a Path,
22    result: &'a AnalysisResult,
23    dry_run: bool,
24}
25
26impl<'a> CleanupCommand<'a> {
27    #[must_use]
28    pub const fn new(root: &'a Path, result: &'a AnalysisResult, dry_run: bool) -> Self {
29        Self {
30            root,
31            result,
32            dry_run,
33        }
34    }
35
36    /// Report the stale entries, and remove them unless this is a dry run.
37    ///
38    /// # Errors
39    ///
40    /// Returns [`crate::cli::cli_error::CliError::Io`] if the ignore file
41    /// cannot be written.
42    pub fn run(&self) -> CliResult<CommandOutcome> {
43        let ignore_file = IgnoreFile::load(self.root);
44        let taken;
45
46        let report = if self.dry_run {
47            StaleReport::dry_run(ignore_file.stale(&self.result.all_fingerprints))
48        } else {
49            let pruned;
50            (pruned, taken) = ignore_file.without_stale(&self.result.all_fingerprints);
51            if !taken.is_empty() {
52                pruned.save(self.root)?;
53            }
54            StaleReport::removed(taken.iter().collect())
55        };
56
57        let mut out = String::new();
58        if report.is_empty() {
59            let _ = writeln!(out, "No stale entries found.");
60            return Ok(CommandOutcome::succeeded(out));
61        }
62
63        let _ = writeln!(out, "{}", report.heading());
64        for entry in report.entries() {
65            let _ = writeln!(out, "{}", IgnoreEntryLine::new(entry));
66        }
67        let _ = writeln!(out, "{}", report.summary());
68        Ok(CommandOutcome::succeeded(out))
69    }
70}