Skip to main content

anvil/
args.rs

1use crate::opts::{Anvil, AnvilSubcommand};
2use clap::{CommandFactory, Parser};
3use eyre::Result;
4use foundry_cli::utils;
5
6/// Run the `anvil` command line interface.
7pub fn run() -> Result<()> {
8    // Pre-parse discovery flags run before `setup()` so they cannot be blocked
9    // by panic-handler / tracing init failures and avoid that init's cost.
10    foundry_cli::opts::GlobalArgs::check_introspect::<Anvil>();
11    foundry_cli::opts::GlobalArgs::check_markdown_help::<Anvil>();
12
13    setup()?;
14
15    let mut args = Anvil::parse();
16    args.global.init()?;
17    args.node.evm.resolve_rpc_alias();
18
19    run_command(args)
20}
21
22/// Setup the exception handler and other utilities.
23pub fn setup() -> Result<()> {
24    utils::common_setup();
25
26    Ok(())
27}
28
29/// Run the subcommand.
30pub fn run_command(args: Anvil) -> Result<()> {
31    if let Some(cmd) = &args.cmd {
32        match cmd {
33            AnvilSubcommand::Completions { shell } => {
34                clap_complete::generate(
35                    *shell,
36                    &mut Anvil::command(),
37                    "anvil",
38                    &mut std::io::stdout(),
39                );
40            }
41        }
42        return Ok(());
43    }
44
45    let _ = fdlimit::raise_fd_limit();
46    args.global.tokio_runtime().block_on(args.node.run())
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn verify_cli() {
55        Anvil::command().debug_assert();
56    }
57
58    #[test]
59    fn can_parse_help() {
60        let _: Anvil = Anvil::parse_from(["anvil", "--help"]);
61    }
62
63    #[test]
64    fn can_parse_short_version() {
65        let _: Anvil = Anvil::parse_from(["anvil", "-V"]);
66    }
67
68    #[test]
69    fn can_parse_long_version() {
70        let _: Anvil = Anvil::parse_from(["anvil", "--version"]);
71    }
72
73    #[test]
74    fn can_parse_completions() {
75        let args: Anvil = Anvil::parse_from(["anvil", "completions", "bash"]);
76        assert!(matches!(
77            args.cmd,
78            Some(AnvilSubcommand::Completions {
79                shell: foundry_cli::clap::Shell::ClapCompleteShell(clap_complete::Shell::Bash)
80            })
81        ));
82    }
83
84    /// Every `command_id` exposed by `anvil --introspect` MUST be unique.
85    #[test]
86    fn introspect_command_ids_are_unique() {
87        use foundry_cli::introspect::{CommandRegistry, build_document, duplicate_command_ids};
88        let cmd = <Anvil as clap::CommandFactory>::command();
89        let doc = build_document(&cmd, &CommandRegistry::EMPTY);
90        let dups = duplicate_command_ids(&doc);
91        assert!(dups.is_empty(), "duplicate anvil command_ids: {dups:?}");
92    }
93
94    /// `anvil --introspect` must produce a JSON document that parses back into
95    /// the canonical `IntrospectDocument` shape.
96    #[test]
97    fn introspect_document_is_valid_json() {
98        use foundry_cli::introspect::{
99            CommandRegistry, INTROSPECT_SCHEMA_ID, IntrospectDocument, render_introspect_document,
100        };
101        let cmd = <Anvil as clap::CommandFactory>::command();
102        let json = render_introspect_document(&cmd, &CommandRegistry::EMPTY);
103        let doc: IntrospectDocument = serde_json::from_str(&json).expect("valid JSON");
104        assert_eq!(doc.schema_id, INTROSPECT_SCHEMA_ID);
105        assert_eq!(doc.binary.name, "anvil");
106    }
107}