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    /// Format log messages as Markdown.
34    #[arg(
35        help_heading = "Display options",
36        global = true,
37        long,
38        alias = "markdown",
39        conflicts_with = "json"
40    )]
41    md: bool,
42
43    /// The color of the log messages.
44    #[arg(help_heading = "Display options", global = true, long, value_enum)]
45    color: Option<ColorChoice>,
46
47    /// Number of threads to use. Specifying 0 defaults to the number of logical cores.
48    #[arg(global = true, long, short = 'j', visible_alias = "jobs")]
49    threads: Option<usize>,
50}
51
52impl GlobalArgs {
53    /// Initialize the global options.
54    pub fn init(&self) -> eyre::Result<()> {
55        // Set the global shell.
56        self.shell().set();
57
58        // Initialize the thread pool only if `threads` was requested to avoid unnecessary overhead.
59        if self.threads.is_some() {
60            self.force_init_thread_pool()?;
61        }
62
63        // Display a warning message if the current version is not stable.
64        if std::env::var("FOUNDRY_DISABLE_NIGHTLY_WARNING").is_err()
65            && !self.json
66            && IS_NIGHTLY_VERSION
67        {
68            let _ = sh_warn!("{}", NIGHTLY_VERSION_WARNING_MESSAGE);
69        }
70
71        Ok(())
72    }
73
74    /// Create a new shell instance.
75    pub fn shell(&self) -> Shell {
76        let mode = match self.quiet {
77            true => OutputMode::Quiet,
78            false => OutputMode::Normal,
79        };
80        let color = self.json.then_some(ColorChoice::Never).or(self.color).unwrap_or_default();
81        let format = if self.json {
82            OutputFormat::Json
83        } else if self.md {
84            OutputFormat::Markdown
85        } else {
86            OutputFormat::Text
87        };
88
89        Shell::new_with(format, mode, color, self.verbosity)
90    }
91
92    /// Initialize the global thread pool.
93    pub fn force_init_thread_pool(&self) -> eyre::Result<()> {
94        init_thread_pool(self.threads.unwrap_or(0))
95    }
96
97    /// Creates a new tokio runtime.
98    #[track_caller]
99    pub fn tokio_runtime(&self) -> tokio::runtime::Runtime {
100        let mut builder = tokio::runtime::Builder::new_multi_thread();
101        if let Some(threads) = self.threads
102            && threads > 0
103        {
104            builder.worker_threads(threads);
105        }
106        builder.enable_all().build().expect("failed to create tokio runtime")
107    }
108
109    /// Creates a new tokio runtime and blocks on the future.
110    #[track_caller]
111    pub fn block_on<F: std::future::Future>(&self, future: F) -> F::Output {
112        self.tokio_runtime().block_on(future)
113    }
114}
115
116/// Initialize the global thread pool.
117pub fn init_thread_pool(threads: usize) -> eyre::Result<()> {
118    rayon::ThreadPoolBuilder::new()
119        .thread_name(|i| format!("foundry-{i}"))
120        .num_threads(threads)
121        .build_global()?;
122    Ok(())
123}