cargo-upkeep 0.1.7

Unified Rust project maintenance CLI (cargo subcommand)
//! Command dispatch and handlers.

mod audit;
mod deps;
mod detect;
mod quality;
mod run_with;
mod tree;
mod unsafe_code;
mod unused;

use crate::core::error::{ErrorCode, Result, UpkeepError};

use crate::cli::UpkeepCommand;

pub async fn handle(command: UpkeepCommand, json: bool) -> Result<()> {
    match command {
        UpkeepCommand::Detect => tokio::task::spawn_blocking(move || detect::run(json))
            .await
            .map_err(|err| {
                // JoinError occurs when:
                // 1. The task panicked (is_panic() returns true)
                // 2. The task was cancelled (is_cancelled() returns true)
                // Note: Testing JoinError paths is complex as it requires injecting panics
                // into spawn_blocking tasks, which is not straightforward to do reliably.
                let reason = if err.is_panic() {
                    "task panicked"
                } else if err.is_cancelled() {
                    "task was cancelled"
                } else {
                    "task failed"
                };
                UpkeepError::message(ErrorCode::TaskFailed, format!("detect {reason}: {err}"))
            })?,
        UpkeepCommand::Audit => audit::run(json).await,
        UpkeepCommand::Deps { security } => deps::run(json, security).await,
        UpkeepCommand::Quality => quality::run(json).await,
        UpkeepCommand::Unused => unused::run(json).await,
        UpkeepCommand::UnsafeCode => unsafe_code::run(json).await,
        UpkeepCommand::Tree(args) => tree::run(json, args).await,
    }
}

#[cfg(test)]
mod tests {
    use super::handle;
    use crate::cli::UpkeepCommand;

    #[tokio::test]
    async fn handlers_return_ok() {
        // Only test commands that don't require network access.
        // Audit requires fetching rustsec advisory database.
        // Deps requires fetching crate info from crates.io.
        // Unused requires cargo-machete to be installed.
        // Unsafe code requires cargo-geiger to be installed.
        let commands = [
            UpkeepCommand::Detect,
            UpkeepCommand::Quality,
            UpkeepCommand::Tree(crate::cli::TreeArgs {
                depth: Some(0),
                duplicates: false,
                invert: None,
                features: false,
                no_dev: false,
            }),
        ];

        for command in commands {
            handle(command, false).await.unwrap();
        }
    }

    #[tokio::test]
    async fn detect_handler_supports_json_output() {
        handle(UpkeepCommand::Detect, true).await.unwrap();
    }
}