foundry_cli/opts/
global.rs

1use clap::{ArgAction, Parser};
2use foundry_common::{
3    shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbosity},
4    version::{IS_NIGHTLY_VERSION, NIGHTLY_VERSION_WARNING_MESSAGE},
5};
6use serde::{Deserialize, Serialize};
7
8/// Global arguments for the CLI.
9#[derive(Clone, Debug, Default, Serialize, Deserialize, Parser)]
10pub struct GlobalArgs {
11    /// Verbosity level of the log messages.
12    ///
13    /// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).
14    ///
15    /// Depending on the context the verbosity levels have different meanings.
16    ///
17    /// For example, the verbosity levels of the EVM are:
18    /// - 2 (-vv): Print logs for all tests.
19    /// - 3 (-vvv): Print execution traces for failing tests.
20    /// - 4 (-vvvv): Print execution traces for all tests, and setup traces for failing tests.
21    /// - 5 (-vvvvv): Print execution and setup traces for all tests, including storage changes.
22    #[arg(help_heading = "Display options", global = true, short, long, verbatim_doc_comment, conflicts_with = "quiet", action = ArgAction::Count)]
23    verbosity: Verbosity,
24
25    /// Do not print log messages.
26    #[arg(help_heading = "Display options", global = true, short, long, alias = "silent")]
27    quiet: bool,
28
29    /// Format log messages as JSON.
30    #[arg(help_heading = "Display options", global = true, long, alias = "format-json", conflicts_with_all = &["quiet", "color"])]
31    json: bool,
32
33    /// The color of the log messages.
34    #[arg(help_heading = "Display options", global = true, long, value_enum)]
35    color: Option<ColorChoice>,
36
37    /// Number of threads to use. Specifying 0 defaults to the number of logical cores.
38    #[arg(global = true, long, short = 'j', visible_alias = "jobs")]
39    threads: Option<usize>,
40}
41
42impl GlobalArgs {
43    /// Initialize the global options.
44    pub fn init(&self) -> eyre::Result<()> {
45        // Set the global shell.
46        self.shell().set();
47
48        // Initialize the thread pool only if `threads` was requested to avoid unnecessary overhead.
49        if self.threads.is_some() {
50            self.force_init_thread_pool()?;
51        }
52
53        // Display a warning message if the current version is not stable.
54        if std::env::var("FOUNDRY_DISABLE_NIGHTLY_WARNING").is_err() &&
55            !self.json &&
56            IS_NIGHTLY_VERSION
57        {
58            let _ = sh_warn!("{}", NIGHTLY_VERSION_WARNING_MESSAGE);
59        }
60
61        Ok(())
62    }
63
64    /// Create a new shell instance.
65    pub fn shell(&self) -> Shell {
66        let mode = match self.quiet {
67            true => OutputMode::Quiet,
68            false => OutputMode::Normal,
69        };
70        let color = self.json.then_some(ColorChoice::Never).or(self.color).unwrap_or_default();
71        let format = match self.json {
72            true => OutputFormat::Json,
73            false => OutputFormat::Text,
74        };
75
76        Shell::new_with(format, mode, color, self.verbosity)
77    }
78
79    /// Initialize the global thread pool.
80    pub fn force_init_thread_pool(&self) -> eyre::Result<()> {
81        init_thread_pool(self.threads.unwrap_or(0))
82    }
83}
84
85/// Initialize the global thread pool.
86pub fn init_thread_pool(threads: usize) -> eyre::Result<()> {
87    rayon::ThreadPoolBuilder::new()
88        .thread_name(|i| format!("foundry-{i}"))
89        .num_threads(threads)
90        .build_global()?;
91    Ok(())
92}