Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to Diskern are documented here. The format follows

### Fixed

- `diskern scan` now rejects missing and nonexistent roots instead of
reporting a successful empty scan
- A relative scan root no longer hides every finding a root-anchored rule
would have made. `diskern scan tmp` from `/var` reported nothing to
clean; roots are resolved to absolute paths before the walk
Expand Down
38 changes: 36 additions & 2 deletions crates/diskern-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand, ValueEnum};
use diskern_core::{report, rules::RulesDb, scanner, Category, Finding, Verdict};
use std::path::PathBuf;
Expand All @@ -19,6 +19,7 @@ enum Command {
/// Read-only scan: find duplicates, caches, and reclaimable space.
Scan {
/// Directories to scan
#[arg(required = true)]
roots: Vec<PathBuf>,
/// Emit full JSON report instead of a summary
#[arg(long)]
Expand Down Expand Up @@ -217,6 +218,15 @@ fn load_rules(path: Option<&std::path::Path>) -> Result<RulesDb> {
Ok(rules.with_embedded_protected_rules())
}

fn validate_roots(roots: &[PathBuf]) -> Result<()> {
for root in roots {
if !root.try_exists()? {
bail!("scan root does not exist: {}", root.display());
}
}
Ok(())
}

fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Expand All @@ -227,6 +237,7 @@ fn main() -> Result<()> {
verdict,
rules,
} => {
validate_roots(&roots)?;
let external_rules = rules.as_deref();
let rules_db = load_rules(external_rules)?;
let opts = scanner::ScanOptions {
Expand Down Expand Up @@ -305,7 +316,30 @@ fn main() -> Result<()> {

#[cfg(test)]
mod tests {
use super::{human_bytes, plural};
use super::{human_bytes, plural, validate_roots, Cli};
use clap::{error::ErrorKind, Parser};
use std::path::PathBuf;

#[test]
fn scan_requires_at_least_one_root() {
let error = match Cli::try_parse_from(["diskern", "scan"]) {
Ok(_) => panic!("scan without roots should be rejected"),
Err(error) => error,
};

assert_eq!(error.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
fn nonexistent_scan_root_is_rejected_before_scanning() {
let missing = PathBuf::from("diskern-test-root-that-does-not-exist");
let error = validate_roots(std::slice::from_ref(&missing)).unwrap_err();

assert_eq!(
error.to_string(),
format!("scan root does not exist: {}", missing.display())
);
}

#[test]
fn plural_returns_empty_only_for_singular() {
Expand Down