forge-guard 0.3.2

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! `forge-guard gas` — analyze gas usage.

use super::GasArgs;
use anyhow::{Context, Result};
use colored::*;

/// Analyze gas usage of smart contracts.
pub fn run(args: &GasArgs) -> Result<()> {
    eprintln!("{}", "⛽ Forge Guard — Gas Analysis".bold());

    // Run forge snapshot for gas report
    let mut cmd = std::process::Command::new("forge");
    cmd.arg("snapshot");

    if let Some(contract) = &args.contract {
        cmd.arg("--match-contract").arg(contract);
    }

    if args.all {
        cmd.arg("--all");
    }

    eprintln!("\n🚀 Running gas analysis...\n");
    let output = cmd.output().context("Failed to run forge snapshot")?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        anyhow::bail!("Gas analysis failed: {}", stderr);
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    println!("{}", stdout);

    // Parse and highlight high gas usage
    eprintln!("{}", "\n── Gas Summary ──".bold());
    eprintln!(
        "  ⚠️  Functions exceeding {} gas threshold will need optimization",
        args.warn_threshold
    );

    for line in stdout.lines() {
        if let Some(gas_str) = line.split('|').nth(2) {
            if let Ok(gas) = gas_str.trim().replace(',', "").parse::<u64>() {
                if gas > args.warn_threshold {
                    println!(
                        "  {} {}{} gas",
                        "⚠️".yellow(),
                        line.split('|').next().unwrap_or("?").trim(),
                        gas
                    );
                }
            }
        }
    }

    if let Some(diff_path) = &args.diff {
        eprintln!("\n📊 Gas diff against {} requested", diff_path);
        // Future: implement diff comparison
    }

    eprintln!("\n{}", "✅ Gas analysis complete.".green().bold());
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gas_arg_defaults() {
        let args = GasArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: false,
                production: false,
                report: false,
                parallelism: 4,
            },
            contract: None,
            diff: None,
            all: false,
            warn_threshold: 50_000,
        };
        assert!(args.contract.is_none());
        assert!(args.diff.is_none());
        assert!(!args.all);
        assert_eq!(args.warn_threshold, 50_000);
    }

    #[test]
    fn test_gas_with_contract_and_threshold() {
        let args = GasArgs {
            shared: super::super::SharedFlags::default(),
            contract: Some("Counter".into()),
            diff: Some("previous.json".into()),
            all: true,
            warn_threshold: 100_000,
        };
        assert_eq!(args.contract.as_deref(), Some("Counter"));
        assert_eq!(args.diff.as_deref(), Some("previous.json"));
        assert!(args.all);
        assert_eq!(args.warn_threshold, 100_000);
    }
}