Skip to main content

anvil/
args.rs

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