Skip to main content

ferro_cli/commands/
clean.rs

1use std::process::Command;
2
3/// Clean build artifacts. Runs `cargo clean` by default.
4/// With `--sweep`, removes only artifacts older than N days (requires cargo-sweep).
5pub fn run(sweep_days: Option<u32>) {
6    match sweep_days {
7        Some(days) => run_sweep(days),
8        None => run_cargo_clean(),
9    }
10}
11
12fn run_cargo_clean() {
13    println!("Cleaning build artifacts...");
14
15    let output = match Command::new("cargo").arg("clean").output() {
16        Ok(o) => o,
17        Err(e) => {
18            eprintln!("Failed to run cargo clean: {e}");
19            return;
20        }
21    };
22
23    if output.status.success() {
24        println!("Build artifacts removed.");
25    } else {
26        let stderr = String::from_utf8_lossy(&output.stderr);
27        eprintln!("cargo clean failed: {stderr}");
28    }
29}
30
31fn run_sweep(days: u32) {
32    if !is_sweep_installed() {
33        eprintln!("cargo-sweep is required for time-based cleanup.");
34        eprintln!("Install it with: cargo install cargo-sweep");
35        return;
36    }
37
38    println!("Cleaning build artifacts older than {days} days...");
39
40    let output = match Command::new("cargo")
41        .args(["sweep", "--time", &days.to_string()])
42        .output()
43    {
44        Ok(o) => o,
45        Err(e) => {
46            eprintln!("Failed to run cargo sweep: {e}");
47            return;
48        }
49    };
50
51    if output.status.success() {
52        let stdout = String::from_utf8_lossy(&output.stdout);
53        if let Some(cleaned) = parse_sweep_output(&stdout) {
54            println!("{cleaned}");
55        } else {
56            println!("Cleanup complete.");
57        }
58    } else {
59        let stderr = String::from_utf8_lossy(&output.stderr);
60        eprintln!("Sweep failed: {stderr}");
61    }
62}
63
64/// Run sweep silently during serve startup, return cleaned size if any
65pub fn run_silent(days: u32) -> Option<String> {
66    if !is_sweep_installed() {
67        return None;
68    }
69
70    let output = Command::new("cargo")
71        .args(["sweep", "--time", &days.to_string()])
72        .output()
73        .ok()?;
74
75    if output.status.success() {
76        let stdout = String::from_utf8_lossy(&output.stdout);
77        parse_sweep_output(&stdout)
78    } else {
79        None
80    }
81}
82
83fn is_sweep_installed() -> bool {
84    Command::new("cargo")
85        .args(["sweep", "--version"])
86        .output()
87        .map(|o| o.status.success())
88        .unwrap_or(false)
89}
90
91fn parse_sweep_output(output: &str) -> Option<String> {
92    for line in output.lines() {
93        if line.contains("Cleaned")
94            && (line.contains("GiB") || line.contains("MiB") || line.contains("KiB"))
95        {
96            if let Some(start) = line.find("Cleaned") {
97                let size_part = &line[start..];
98                return Some(size_part.to_string());
99            }
100        }
101    }
102    None
103}