forge-guard 0.1.0

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! `forge doctor` — analyze project health.

use crate::core::ProjectConfig;
use crate::doctor::Doctor;

use super::DoctorArgs;
use anyhow::Result;
use colored::*;

/// Analyze project health and configuration.
pub fn run(args: &DoctorArgs) -> Result<()> {
    let config = ProjectConfig::from_default_location();
    let mut doctor = Doctor::new(&config, args.verbose)?;

    eprintln!("{}", "🩺 Forge Guard — Project Doctor".bold());
    eprintln!("   Analyzing project health...\n");

    // Run all checks
    let mut all_pass = true;

    // 1. Foundry version check
    print_check("Foundry installation");
    match doctor.check_foundry_version() {
        Ok(version) => println!("  {} Foundry {}\n", "".green(), version),
        Err(e) => {
            println!("  {} {}\n", "".red(), e);
            all_pass = false;
        }
    }

    // 2. Solidity version check
    print_check("Solidity version");
    match doctor.check_solidity_version() {
        Ok(version) => println!("  {} Solidity {}\n", "".green(), version),
        Err(e) => {
            println!("  {} {}\n", "".red(), e);
            all_pass = false;
        }
    }

    // 3. Project structure
    print_check("Project structure");
    let structure_issues = doctor.check_project_structure()?;
    if structure_issues.is_empty() {
        println!("  {} Project structure looks good\n", "".green());
    } else {
        for issue in &structure_issues {
            println!("  {} {}\n", "⚠️".yellow(), issue);
        }
    }

    // 4. Dependency analysis
    print_check("Dependency vulnerabilities");
    let dep_issues = doctor.check_dependencies()?;
    if dep_issues.is_empty() {
        println!("  {} No vulnerable dependencies found\n", "".green());
    } else {
        for issue in &dep_issues {
            println!("  {} {}\n", "⚠️".yellow(), issue);
        }
    }

    // 5. Compiler settings
    print_check("Compiler settings");
    let compiler_issues = doctor.check_compiler_settings()?;
    if compiler_issues.is_empty() {
        println!("  {} Compiler settings OK\n", "".green());
    } else {
        for issue in &compiler_issues {
            println!("  {} {}\n", "⚠️".yellow(), issue);
        }
    }

    // 6. RPC configuration
    if !config.offline {
        print_check("RPC connectivity");
        match doctor.check_rpc_connectivity(&config.chain) {
            Ok(_) => println!("  {} RPC reachable\n", "".green()),
            Err(e) => println!("  {} {}\n", "".red(), e),
        }
    }

    // 7. Security configuration
    print_check("Security configuration");
    let sec_issues = doctor.check_security_config()?;
    if sec_issues.is_empty() {
        println!("  {} Security configuration OK\n", "".green());
    } else {
        for issue in &sec_issues {
            println!("  {} {}\n", "⚠️".yellow(), issue);
        }
    }

    // Summary
    println!(
        "{}",
        "═══════════════════════════════════════".bright_blue()
    );
    if all_pass {
        println!("{}", "✅ Project health: GOOD".green().bold());
    } else {
        println!("{}", "⚠️  Project health: ISSUES FOUND".yellow().bold());
    }
    println!(
        "{}",
        "═══════════════════════════════════════".bright_blue()
    );

    // Output as JSON if requested
    if args.shared.json {
        let report = doctor.generate_report(all_pass);
        println!("{}", serde_json::to_string_pretty(&report)?);
    }

    Ok(())
}

fn print_check(label: &str) {
    print!("  🔍 {}... ", label.bold());
    std::io::Write::flush(&mut std::io::stdout()).ok();
}