From a663e846abe14b13021d626861d24cbe31f72889 Mon Sep 17 00:00:00 2001 From: maciektr Date: Fri, 5 Apr 2024 13:51:14 +0200 Subject: [PATCH] Read ui verbosity from env var --- Cargo.lock | 1 + Cargo.toml | 1 + extensions/scarb-cairo-run/src/main.rs | 18 ++-- extensions/scarb-cairo-run/tests/arguments.rs | 23 +++++ scarb/src/bin/scarb/args.rs | 18 +--- scarb/src/bin/scarb/commands/metadata.rs | 2 +- scarb/src/bin/scarb/main.rs | 8 +- scarb/tests/subcommand.rs | 31 ++++++- utils/scarb-ui/Cargo.toml | 1 + utils/scarb-ui/src/args/mod.rs | 2 + utils/scarb-ui/src/args/verbosity.rs | 85 +++++++++++++++++++ utils/scarb-ui/src/lib.rs | 5 ++ 12 files changed, 164 insertions(+), 31 deletions(-) create mode 100644 utils/scarb-ui/src/args/verbosity.rs diff --git a/Cargo.lock b/Cargo.lock index ca0ed57a3..b33898317 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4730,6 +4730,7 @@ dependencies = [ "scarb-metadata 1.11.1", "serde", "serde_json", + "tracing-core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 63ad4f63d..6a79476c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,6 +120,7 @@ toml_edit = { version = "0.22", features = ["serde"] } tower-http = { version = "0.4", features = ["fs"] } tracing = "0.1" tracing-log = "0.2" +tracing-core = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } typed-builder = ">=0.17" url = { version = "2", features = ["serde"] } diff --git a/extensions/scarb-cairo-run/src/main.rs b/extensions/scarb-cairo-run/src/main.rs index cc29c90e7..0061717f3 100644 --- a/extensions/scarb-cairo-run/src/main.rs +++ b/extensions/scarb-cairo-run/src/main.rs @@ -13,9 +13,9 @@ use serde::Serializer; use scarb_metadata::{ CompilationUnitMetadata, Metadata, MetadataCommand, PackageId, PackageMetadata, ScarbCommand, }; -use scarb_ui::args::PackagesFilter; +use scarb_ui::args::{PackagesFilter, VerbositySpec}; use scarb_ui::components::Status; -use scarb_ui::{Message, OutputFormat, Ui, Verbosity}; +use scarb_ui::{Message, OutputFormat, Ui}; mod deserialization; @@ -39,6 +39,10 @@ struct Args { #[arg(long, default_value_t = false)] no_build: bool, + /// Logging verbosity. + #[command(flatten)] + pub verbose: VerbositySpec, + /// Program arguments. /// /// This should be a JSON array of numbers, decimal bigints or recursive arrays of those. For example, pass `[1]` @@ -49,18 +53,16 @@ struct Args { } fn main() -> Result<()> { - let ui = Ui::new(Verbosity::default(), OutputFormat::Text); - - if let Err(err) = main_inner(&ui) { + let args: Args = Args::parse(); + let ui = Ui::new(args.verbose.clone().into(), OutputFormat::Text); + if let Err(err) = main_inner(&ui, args) { ui.anyhow(&err); std::process::exit(1); } Ok(()) } -fn main_inner(ui: &Ui) -> Result<()> { - let args: Args = Args::parse(); - +fn main_inner(ui: &Ui, args: Args) -> Result<()> { let metadata = MetadataCommand::new().inherit_stderr().exec()?; let package = args.packages_filter.match_one(&metadata)?; diff --git a/extensions/scarb-cairo-run/tests/arguments.rs b/extensions/scarb-cairo-run/tests/arguments.rs index 108faed06..d7e5e3f55 100644 --- a/extensions/scarb-cairo-run/tests/arguments.rs +++ b/extensions/scarb-cairo-run/tests/arguments.rs @@ -335,3 +335,26 @@ fn cannot_set_gas_limit_for_package_with_disabled_gas_calculation() { error: gas calculation disabled for package `hello`, cannot define custom gas limit "#}); } + +#[test] +fn can_control_verbosity() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("0.1.0") + .lib_cairo(indoc! {r#" + fn main() { + println!("something"); + } + "#}) + .build(&t); + Scarb::quick_snapbox() + .arg("--quiet") + .arg("cairo-run") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + something + "#}); +} diff --git a/scarb/src/bin/scarb/args.rs b/scarb/src/bin/scarb/args.rs index 980dff6ea..f0865ee3c 100644 --- a/scarb/src/bin/scarb/args.rs +++ b/scarb/src/bin/scarb/args.rs @@ -10,8 +10,6 @@ use camino::Utf8PathBuf; use clap::{CommandFactory, Parser, Subcommand}; use scarb::ops::EmitTarget; use smol_str::SmolStr; -use tracing::level_filters::LevelFilter; -use tracing_log::AsTrace; use url::Url; use scarb::compiler::Profile; @@ -19,7 +17,7 @@ use scarb::core::PackageName; use scarb::manifest_editor::DepId; use scarb::manifest_editor::SectionArgs; use scarb::version; -use scarb_ui::args::{FeaturesSpec, PackagesFilter}; +use scarb_ui::args::{FeaturesSpec, PackagesFilter, VerbositySpec}; use scarb_ui::OutputFormat; /// The Cairo package manager. @@ -62,7 +60,7 @@ pub struct ScarbArgs { /// Logging verbosity. #[command(flatten)] - pub verbose: clap_verbosity_flag::Verbosity, + pub verbose: VerbositySpec, /// Print machine-readable output in NDJSON format. #[arg(long)] @@ -118,18 +116,6 @@ impl ScarbArgs { } } - /// Get [`ui::Verbosity`] out of these arguments. - pub fn ui_verbosity(&self) -> scarb_ui::Verbosity { - let filter = self.verbose.log_level_filter().as_trace(); - if filter >= LevelFilter::WARN { - scarb_ui::Verbosity::Verbose - } else if filter > LevelFilter::OFF { - scarb_ui::Verbosity::Normal - } else { - scarb_ui::Verbosity::Quiet - } - } - pub fn get_builtin_subcommands() -> BTreeMap> { Self::command() .get_subcommands() diff --git a/scarb/src/bin/scarb/commands/metadata.rs b/scarb/src/bin/scarb/commands/metadata.rs index 6cf98cfad..437cee94f 100644 --- a/scarb/src/bin/scarb/commands/metadata.rs +++ b/scarb/src/bin/scarb/commands/metadata.rs @@ -19,7 +19,7 @@ pub fn run(args: MetadataArgs, config: &Config) -> Result<()> { let metadata = ops::collect_metadata(&opts, &ws)?; - config.ui().print(MachineMessage(metadata)); + config.ui().force_print(MachineMessage(metadata)); Ok(()) } diff --git a/scarb/src/bin/scarb/main.rs b/scarb/src/bin/scarb/main.rs index 9e0de2893..244760615 100644 --- a/scarb/src/bin/scarb/main.rs +++ b/scarb/src/bin/scarb/main.rs @@ -3,7 +3,6 @@ use std::env; use anyhow::{Error, Result}; use clap::Parser; use tracing::debug; -use tracing_log::AsTrace; use tracing_subscriber::EnvFilter; use args::ScarbArgs; @@ -22,13 +21,13 @@ fn main() { let args = ScarbArgs::parse(); // Pre-create Ui used in logging & error reporting, because we will move `args` to `cli_main`. - let ui = Ui::new(args.ui_verbosity(), args.output_format()); + let ui = Ui::new(args.verbose.clone().into(), args.output_format()); tracing_subscriber::fmt() .with_writer(std::io::stderr) .with_env_filter( EnvFilter::builder() - .with_default_directive(args.verbose.log_level_filter().as_trace().into()) + .with_default_directive(args.verbose.as_trace().into()) .with_env_var("SCARB_LOG") .from_env_lossy(), ) @@ -59,7 +58,6 @@ fn exit_with_error(err: Error, ui: &Ui) { } fn cli_main(args: ScarbArgs) -> Result<()> { - let ui_verbosity = args.ui_verbosity(); let ui_output_format = args.output_format(); let manifest_path = ops::find_manifest_path(args.manifest_path.as_deref())?; @@ -68,7 +66,7 @@ fn cli_main(args: ScarbArgs) -> Result<()> { .global_cache_dir_override(args.global_cache_dir) .global_config_dir_override(args.global_config_dir) .target_dir_override(args.target_dir) - .ui_verbosity(ui_verbosity) + .ui_verbosity(args.verbose.clone().into()) .ui_output_format(ui_output_format) .offline(args.offline) .log_filter_directive(env::var_os("SCARB_LOG")) diff --git a/scarb/tests/subcommand.rs b/scarb/tests/subcommand.rs index e1ae32e04..20fcc1c30 100644 --- a/scarb/tests/subcommand.rs +++ b/scarb/tests/subcommand.rs @@ -4,7 +4,6 @@ use std::process::{Child, Command}; use std::{env, io}; use assert_fs::TempDir; -#[cfg(unix)] use indoc::indoc; use scarb_test_support::cargo::cargo_bin; @@ -215,3 +214,33 @@ fn can_list_scarb_directory_scripts() { let stdout = String::from_utf8(output).unwrap(); assert!(stdout.contains("hello")) } + +#[test] +fn scarb_reads_verbosity_from_env() { + let p = TempDir::new().unwrap(); + ProjectBuilder::start().build(&p); + Scarb::quick_snapbox() + .current_dir(&p) + .arg("check") + .env("SCARB_UI_VERBOSITY", "quiet") + .assert() + .success() + .stdout_eq(""); +} + +#[test] +fn cli_verbosity_overrides_env() { + let p = TempDir::new().unwrap(); + ProjectBuilder::start().build(&p); + Scarb::quick_snapbox() + .current_dir(&p) + .arg("check") + .arg("--verbose") + .env("SCARB_UI_VERBOSITY", "quiet") + .assert() + .success() + .stdout_matches(indoc! {r#" + [..]Checking pkg0 v1.0.0 ([..]Scarb.toml) + [..]Finished checking release target(s) in [..] + "#}); +} diff --git a/utils/scarb-ui/Cargo.toml b/utils/scarb-ui/Cargo.toml index e905f5c9d..c46239f1e 100644 --- a/utils/scarb-ui/Cargo.toml +++ b/utils/scarb-ui/Cargo.toml @@ -21,3 +21,4 @@ indicatif.workspace = true scarb-metadata = { version = "1", path = "../../scarb-metadata" } serde.workspace = true serde_json.workspace = true +tracing-core.workspace = true diff --git a/utils/scarb-ui/src/args/mod.rs b/utils/scarb-ui/src/args/mod.rs index 5a5b09e8a..7a01900e5 100644 --- a/utils/scarb-ui/src/args/mod.rs +++ b/utils/scarb-ui/src/args/mod.rs @@ -2,6 +2,8 @@ pub use features::*; pub use packages_filter::*; +pub use verbosity::*; mod features; mod packages_filter; +mod verbosity; diff --git a/utils/scarb-ui/src/args/verbosity.rs b/utils/scarb-ui/src/args/verbosity.rs new file mode 100644 index 000000000..c04be04fd --- /dev/null +++ b/utils/scarb-ui/src/args/verbosity.rs @@ -0,0 +1,85 @@ +use crate::Verbosity; + +/// [`clap`] structured arguments that provide Scarb UI verbosity selection. +#[derive(clap::Args, Debug, Clone, Default)] +#[command(about = None, long_about = None)] +pub struct VerbositySpec { + #[arg( + long, + short = 'v', + action = clap::ArgAction::Count, + global = true, + help = "Increase logging verbosity.", + )] + verbose: u8, + + #[arg( + long, + short = 'q', + action = clap::ArgAction::Count, + global = true, + help = "Decrease logging verbosity.", + conflicts_with = "verbose", + )] + quiet: u8, + + #[arg( + long, + global = true, + help = "Set UI verbosity level by name.", + env = "SCARB_UI_VERBOSITY" + )] + verbosity: Option, +} + +impl Verbosity { + fn level_value(level: Self) -> i8 { + match level { + Self::Quiet => 0, + Self::Normal => 2, + Self::Verbose => 4, + } + } +} + +impl VerbositySpec { + /// Whether any verbosity flags (either `--verbose` or `--quiet`) + /// are present on the command line. + pub fn is_present(&self) -> bool { + self.verbose != 0 || self.quiet != 0 + } + + /// Convert the verbosity specification to a [`tracing_core::LevelFilter`]. + pub fn as_trace(&self) -> tracing_core::LevelFilter { + match self.integer_verbosity() { + i8::MIN..=-1 => tracing_core::LevelFilter::OFF, + 0 => tracing_core::LevelFilter::ERROR, + 1 => tracing_core::LevelFilter::WARN, + 2 => tracing_core::LevelFilter::INFO, + 3 => tracing_core::LevelFilter::DEBUG, + 4..=i8::MAX => tracing_core::LevelFilter::TRACE, + } + } + + fn integer_verbosity(&self) -> i8 { + let int_level = Verbosity::level_value(Verbosity::default()) - (self.quiet as i8) + + (self.verbose as i8); + if self.is_present() { + int_level + } else { + self.verbosity + .map(Verbosity::level_value) + .unwrap_or(int_level) + } + } +} + +impl From for Verbosity { + fn from(spec: VerbositySpec) -> Self { + match spec.integer_verbosity() { + v if v < 2 => Verbosity::Quiet, + 2 => Verbosity::Normal, + _ => Verbosity::Verbose, + } + } +} diff --git a/utils/scarb-ui/src/lib.rs b/utils/scarb-ui/src/lib.rs index 5278192d2..b128fc730 100644 --- a/utils/scarb-ui/src/lib.rs +++ b/utils/scarb-ui/src/lib.rs @@ -85,6 +85,11 @@ impl Ui { } } + /// Print the message to standard output regardless of the verbosity mode. + pub fn force_print(&self, message: T) { + self.do_print(message); + } + /// Print the message to the standard output only in verbose mode. pub fn verbose(&self, message: T) { if self.verbosity >= Verbosity::Verbose {