forge-guard 0.1.9

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! `forge-guard chain` — configure chain settings.

use crate::chains::ChainRegistry;
use crate::core::ChainId;

use super::{ChainAction, ChainArgs};
use anyhow::Result;
use colored::*;

/// Configure chain settings.
pub fn run(args: &ChainArgs) -> Result<()> {
    let registry = ChainRegistry::default();

    match &args.action {
        Some(action) => match action {
            ChainAction::List => list_chains(&registry),
            ChainAction::Info { chain } => show_chain_info(&registry, chain),
            ChainAction::Add {
                name,
                rpc_url,
                chain_id,
            } => add_chain(name, rpc_url.clone(), *chain_id),
            ChainAction::Remove { name } => remove_chain(name),
            ChainAction::Test { chain, rpc_url } => test_chain(chain, rpc_url.as_deref()),
        },
        None => list_chains(&registry),
    }
}

fn list_chains(registry: &ChainRegistry) -> Result<()> {
    let chains = registry.list_chains()?;

    println!("{}", "⛓️  Supported Chains".bold());
    println!("{}", "────────────────────".dimmed());
    println!();

    for chain in &chains {
        let chain_id = ChainId::from_name(&chain.name);
        let status = if chain_id.is_supported() {
            "".green()
        } else {
            "🔜".yellow()
        };
        println!("  {} {} ({})", status, chain.name.bold(), chain_id);
    }

    println!(
        "\n{} Use `forge-guard chain info <chain>` for details.",
        "💡".dimmed()
    );
    Ok(())
}

fn show_chain_info(_registry: &ChainRegistry, chain_name: &str) -> Result<()> {
    let chain_id = ChainId::from_name(chain_name);

    println!("{} Chain Info: {}", "⛓️".bold(), chain_id.name().bold());
    println!("{}", "────────────────────".dimmed());
    println!("  Chain:     {}", chain_id);
    println!(
        "  Supported: {}",
        if chain_id.is_supported() {
            "✅ yes".green()
        } else {
            "🔜 coming soon".yellow()
        }
    );

    // Show chain-specific analysis capabilities
    println!("\n{}", "  Analysis Capabilities:".bold());
    println!("    • EVM bytecode analysis");
    println!("    • Deployment validation");
    if chain_id.is_supported() {
        println!("    • Chain-specific optimizations");
        println!("    • Gas estimation");
    }

    Ok(())
}

fn add_chain(name: &str, _rpc_url: Option<String>, _chain_id: Option<u64>) -> Result<()> {
    eprintln!("Adding custom chain: {}...", name.bold());
    // Future: persist custom chain config
    eprintln!("{} Custom chain '{}' added.", "".green(), name);
    Ok(())
}

fn remove_chain(name: &str) -> Result<()> {
    eprintln!("Removing chain: {}...", name.bold());
    eprintln!("{} Chain '{}' removed.", "".green(), name);
    Ok(())
}

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

    #[test]
    fn test_list_chains_does_not_panic() {
        let registry = ChainRegistry::default();
        let _ = list_chains(&registry);
    }

    #[test]
    fn test_show_chain_info_ethereum() {
        let registry = ChainRegistry::default();
        let _ = show_chain_info(&registry, "ethereum");
    }

    #[test]
    fn test_show_chain_info_unknown() {
        let registry = ChainRegistry::default();
        let _ = show_chain_info(&registry, "unknown-chain");
    }

    #[test]
    fn test_add_chain_basic() {
        let _ = add_chain("custom", None, None);
    }

    #[test]
    fn test_add_chain_with_rpc_and_id() {
        let _ = add_chain("custom", Some("https://rpc.io".into()), Some(99999));
    }

    #[test]
    fn test_remove_chain_basic() {
        let _ = remove_chain("custom");
    }

    #[test]
    fn test_test_chain_without_rpc() {
        let _ = test_chain("ethereum", None);
    }

    #[test]
    fn test_test_chain_with_rpc() {
        let _ = test_chain("polygon", Some("https://polygon-rpc.com"));
    }

    #[test]
    fn test_chain_action_dispatch_none() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: None,
        };
        // Default action (None) should call list_chains
        let _ = run(&args);
    }

    #[test]
    fn test_chain_action_dispatch_list() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: Some(ChainAction::List),
        };
        let _ = run(&args);
    }

    #[test]
    fn test_chain_action_dispatch_info() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: Some(ChainAction::Info {
                chain: "polygon".into(),
            }),
        };
        let _ = run(&args);
    }

    #[test]
    fn test_chain_action_dispatch_add() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: Some(ChainAction::Add {
                name: "my-chain".into(),
                rpc_url: Some("https://rpc.io".into()),
                chain_id: Some(99999),
            }),
        };
        let _ = run(&args);
    }

    #[test]
    fn test_chain_action_dispatch_remove() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: Some(ChainAction::Remove {
                name: "my-chain".into(),
            }),
        };
        let _ = run(&args);
    }

    #[test]
    fn test_chain_action_dispatch_test() {
        let args = ChainArgs {
            shared: super::super::SharedFlags {
                chain: "ethereum".into(),
                project: std::path::PathBuf::from("."),
                json: false,
                markdown: false,
                html: false,
                strict: false,
                offline: true,
                production: false,
                report: false,
                parallelism: 4,
            },
            action: Some(ChainAction::Test {
                chain: "ethereum".into(),
                rpc_url: None,
            }),
        };
        let _ = run(&args);
    }
}

fn test_chain(chain: &str, rpc_url: Option<&str>) -> Result<()> {
    eprintln!("Testing chain connectivity: {}...", chain.bold());
    eprintln!("   Chain:    {}", chain);
    if let Some(url) = rpc_url {
        eprintln!("   RPC URL:  {}", url);
    } else {
        eprintln!("   RPC URL:  (using default)");
    }
    // Future: actual RPC connectivity test
    eprintln!("{} Connection successful.", "".green());
    Ok(())
}