1use crate::{
2 cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch},
3 opts::{Forge, ForgeSubcommand},
4};
5use clap::{CommandFactory, Parser};
6use clap_complete::generate;
7use eyre::Result;
8use foundry_cli::utils;
9use foundry_common::{sh_warn, shell};
10use foundry_evm::inspectors::cheatcodes::{ForgeContext, set_execution_context};
11
12pub fn run() -> Result<()> {
14 foundry_cli::opts::GlobalArgs::check_introspect::<Forge>();
17 foundry_cli::opts::GlobalArgs::check_markdown_help::<Forge>();
18
19 setup()?;
20
21 let args = Forge::parse();
22 args.global.init()?;
23
24 run_command(args)
25}
26
27pub fn setup() -> Result<()> {
29 utils::common_setup();
30 utils::subscriber();
31
32 Ok(())
33}
34
35pub fn run_command(args: Forge) -> Result<()> {
37 let context = match &args.cmd {
39 ForgeSubcommand::Test(_) => ForgeContext::Test,
40 ForgeSubcommand::Coverage(_) => ForgeContext::Coverage,
41 ForgeSubcommand::Snapshot(_) => ForgeContext::Snapshot,
42 ForgeSubcommand::Script(cmd) => {
43 if cmd.broadcast {
44 ForgeContext::ScriptBroadcast
45 } else if cmd.resume {
46 ForgeContext::ScriptResume
47 } else {
48 ForgeContext::ScriptDryRun
49 }
50 }
51 _ => ForgeContext::Unknown,
52 };
53 set_execution_context(context);
54
55 let global = &args.global;
56
57 match args.cmd {
59 ForgeSubcommand::Test(cmd) => {
60 if cmd.is_watch() {
61 global.block_on(watch::watch_test(cmd))
62 } else {
63 let silent = cmd.junit || shell::is_json();
64 let outcome = global.block_on(cmd.run())?;
65 outcome.ensure_ok(silent)
66 }
67 }
68 ForgeSubcommand::Script(cmd) => global.block_on(cmd.run_script()),
69 ForgeSubcommand::Coverage(cmd) => {
70 if cmd.is_watch() {
71 global.block_on(watch::watch_coverage(cmd))
72 } else {
73 global.block_on(cmd.run())
74 }
75 }
76 ForgeSubcommand::Bind(cmd) => cmd.run(),
77 ForgeSubcommand::Build(cmd) => {
78 if cmd.is_watch() {
79 global.block_on(watch::watch_build(cmd))
80 } else {
81 global.block_on(cmd.run()).map(drop)
82 }
83 }
84 ForgeSubcommand::VerifyContract(args) => global.block_on(args.run()),
85 ForgeSubcommand::VerifyCheck(args) => global.block_on(args.run()),
86 ForgeSubcommand::VerifyBytecode(cmd) => global.block_on(cmd.run()),
87 ForgeSubcommand::Clone(cmd) => global.block_on(cmd.run()),
88 ForgeSubcommand::Cache(cmd) => match cmd.sub {
89 CacheSubcommands::Clean(cmd) => cmd.run(),
90 CacheSubcommands::Ls(cmd) => cmd.run(),
91 },
92 ForgeSubcommand::Create(cmd) => global.block_on(cmd.run()),
93 ForgeSubcommand::Update(cmd) => cmd.run(),
94 ForgeSubcommand::Install(cmd) => global.block_on(cmd.run()),
95 ForgeSubcommand::Remove(cmd) => cmd.run(),
96 ForgeSubcommand::Remappings(cmd) => cmd.run(),
97 ForgeSubcommand::Init(cmd) => global.block_on(cmd.run()),
98 ForgeSubcommand::Completions { shell } => {
99 generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout());
100 Ok(())
101 }
102 ForgeSubcommand::Clean { root } => {
103 let config = utils::load_config_with_root(root.as_deref())?;
104 let project = config.project()?;
105 for warning in config.cleanup(&project)? {
106 let _ = sh_warn!("{warning}");
107 }
108 Ok(())
109 }
110 ForgeSubcommand::Snapshot(cmd) => {
111 if cmd.is_watch() {
112 global.block_on(watch::watch_gas_snapshot(cmd))
113 } else {
114 global.block_on(cmd.run())
115 }
116 }
117 ForgeSubcommand::Fmt(cmd) => {
118 if cmd.is_watch() {
119 global.block_on(watch::watch_fmt(cmd))
120 } else {
121 cmd.run()
122 }
123 }
124 ForgeSubcommand::Config(cmd) => cmd.run(),
125 ForgeSubcommand::Flatten(cmd) => cmd.run(),
126 ForgeSubcommand::Inspect(cmd) => cmd.run(),
127 ForgeSubcommand::Tree(cmd) => cmd.run(),
128 ForgeSubcommand::Geiger(cmd) => cmd.run(),
129 ForgeSubcommand::Doc(cmd) => {
130 if cmd.is_watch() {
131 global.block_on(watch::watch_doc(cmd))
132 } else {
133 global.block_on(cmd.run())?;
134 Ok(())
135 }
136 }
137 ForgeSubcommand::Selectors { command } => global.block_on(command.run()),
138 ForgeSubcommand::Generate(cmd) => match cmd.sub {
139 GenerateSubcommands::Test(cmd) => cmd.run(),
140 },
141 ForgeSubcommand::Compiler(cmd) => cmd.run(),
142 ForgeSubcommand::Soldeer(cmd) => global.block_on(cmd.run()),
143 ForgeSubcommand::Eip712(cmd) => cmd.run(),
144 ForgeSubcommand::BindJson(cmd) => cmd.run(),
145 ForgeSubcommand::Lint(cmd) => cmd.run(),
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use foundry_cli::introspect::{
153 CommandRegistry, INTROSPECT_SCHEMA_ID, IntrospectDocument, build_document,
154 duplicate_command_ids, render_introspect_document,
155 };
156
157 #[test]
162 fn introspect_command_ids_are_unique() {
163 let cmd = <Forge as clap::CommandFactory>::command();
164 let doc = build_document(&cmd, &CommandRegistry::EMPTY);
165 let dups = duplicate_command_ids(&doc);
166 assert!(dups.is_empty(), "duplicate forge command_ids: {dups:?}");
167 }
168
169 #[test]
172 fn introspect_document_is_valid_json() {
173 let cmd = <Forge as clap::CommandFactory>::command();
174 let json = render_introspect_document(&cmd, &CommandRegistry::EMPTY);
175 let doc: IntrospectDocument = serde_json::from_str(&json).expect("valid JSON");
176 assert_eq!(doc.schema_id, INTROSPECT_SCHEMA_ID);
177 assert_eq!(doc.binary.name, "forge");
178 }
179}