rust-doctor 0.2.0

A unified code health tool for Rust — scan, score, and fix your codebase
Documentation
#![forbid(unsafe_code)]
#![allow(clippy::multiple_crate_versions)]

use clap::Parser;
use rust_doctor::cli::{Cli, Command};
use rust_doctor::{config, run};
use std::process::ExitCode;

fn main() -> ExitCode {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .with_writer(std::io::stderr)
        .init();

    let cli = Cli::parse();

    if matches!(cli.command, Some(Command::Setup)) {
        return run::handle_setup();
    }
    if cli.install_deps {
        return run::handle_install_deps();
    }

    // MCP mode
    if let Some(code) = run::handle_mcp_flag(&cli) {
        return code;
    }

    // Bootstrap: resolve directory, discover project, load file config
    let (_target_dir, project_info, file_config) = match run::bootstrap_project(&cli) {
        Ok(result) => result,
        Err(e) => {
            eprintln!("Error: {e}");
            return ExitCode::from(run::EXIT_SCAN_ERROR);
        }
    };

    // Merge CLI flags with file config
    let effective_config = if cli.no_project_config {
        None
    } else {
        file_config.as_ref()
    };
    let resolved = config::resolve_config(&cli, effective_config);

    // Run scan
    let scan_result = match run::run_scan(&cli, &project_info, &resolved) {
        Ok(result) => result,
        Err(e) => {
            eprintln!("Error: {e}");
            return ExitCode::from(run::EXIT_SCAN_ERROR);
        }
    };

    // Apply fixes, emit output, show plan
    run::apply_fixes_if_requested(&cli, &scan_result);

    if let Err(e) = run::emit_output(&cli, &scan_result, &resolved) {
        eprintln!("Error: {e}");
        return ExitCode::from(run::EXIT_SCAN_ERROR);
    }

    run::emit_plan_if_requested(&cli, &scan_result);

    // Quality gates
    if let Some(code) = run::check_score_gate(&scan_result, resolved.score_fail_below) {
        return code;
    }
    if let Some(code) = run::check_fail_on_gate(&scan_result, resolved.fail_on) {
        return code;
    }

    ExitCode::SUCCESS
}