diff --git a/scarb/src/sources/git/client.rs b/scarb/src/sources/git/client.rs index 34b9ec882..c45938abd 100644 --- a/scarb/src/sources/git/client.rs +++ b/scarb/src/sources/git/client.rs @@ -416,7 +416,7 @@ fn git_command() -> Command { fn with_verbosity_flags(cmd: &mut Command, config: &Config) { match config.ui().verbosity() { - Verbosity::Normal => {} + Verbosity::Normal | Verbosity::NoWarnings => {} Verbosity::Verbose => { cmd.arg("--verbose"); } diff --git a/scarb/tests/add.rs b/scarb/tests/add.rs index 472460edf..76bb3bff9 100644 --- a/scarb/tests/add.rs +++ b/scarb/tests/add.rs @@ -110,6 +110,22 @@ fn dry_run() { .run(); } +#[test] +fn dry_run_without_warnings() { + ManifestEditHarness::offline() + .args(["add", "--dry-run", "--no-warnings", "dep@1.0.0"]) + .input(indoc! {r#" + [package] + name = "hello" + version = "1.0.0" + + [dependencies] + bar = "1.0.0" + "#}) + .stdout_matches("") + .run(); +} + #[test] fn path() { let t = TempDir::new().unwrap(); diff --git a/utils/scarb-ui/src/args/verbosity.rs b/utils/scarb-ui/src/args/verbosity.rs index ae0154dd7..ddfd0d908 100644 --- a/utils/scarb-ui/src/args/verbosity.rs +++ b/utils/scarb-ui/src/args/verbosity.rs @@ -23,6 +23,15 @@ pub struct VerbositySpec { )] quiet: u8, + #[arg( + long = "no-warnings", + action = clap::ArgAction::SetTrue, + global = true, + help = "Decrease logging verbosity, hiding warnings but still showing errors.", + conflicts_with_all = &["verbose", "quiet"] + )] + no_warnings: bool, + #[arg( long, global = true, @@ -35,7 +44,8 @@ pub struct VerbositySpec { impl Verbosity { fn level_value(level: Self) -> i8 { match level { - Self::Quiet => -1, + Self::Quiet => -2, + Self::NoWarnings => -1, Self::Normal => 0, Self::Verbose => 1, } @@ -43,16 +53,17 @@ impl Verbosity { } impl VerbositySpec { - /// Whether any verbosity flags (either `--verbose` or `--quiet`) + /// Whether any verbosity flags (either `--verbose`, `--quiet`, or `--no-warnings`) /// are present on the command line. pub fn is_present(&self) -> bool { - self.verbose != 0 || self.quiet != 0 + self.verbose != 0 || self.quiet != 0 || self.no_warnings } /// Convert the verbosity specification to a [`tracing_core::LevelFilter`]. pub fn as_trace(&self) -> String { let level = match self.integer_verbosity() { - i8::MIN..=-1 => tracing_core::LevelFilter::OFF, + i8::MIN..=-2 => tracing_core::LevelFilter::OFF, + -1 => tracing_core::LevelFilter::ERROR, 0 => tracing_core::LevelFilter::ERROR, 1 => tracing_core::LevelFilter::WARN, 2 => tracing_core::LevelFilter::INFO, @@ -63,7 +74,12 @@ impl VerbositySpec { } fn integer_verbosity(&self) -> i8 { - let int_level = (self.verbose as i8) - (self.quiet as i8); + let int_level = if self.no_warnings { + -1 + } else { + (self.verbose as i8) - (self.quiet as i8 * 2) + }; + if self.is_present() { int_level } else { @@ -77,7 +93,8 @@ impl VerbositySpec { impl From for Verbosity { fn from(spec: VerbositySpec) -> Self { match spec.integer_verbosity() { - v if v < 0 => Verbosity::Quiet, + v if v < -1 => Verbosity::Quiet, + -1 => Verbosity::NoWarnings, 0 => Verbosity::Normal, _ => Verbosity::Verbose, } @@ -92,6 +109,7 @@ mod tests { use crate::Verbosity; #[test_case(Verbosity::Quiet)] + #[test_case(Verbosity::NoWarnings)] #[test_case(Verbosity::Normal)] #[test_case(Verbosity::Verbose)] fn verbosity_serialization_identity(level: Verbosity) { @@ -100,22 +118,25 @@ mod tests { verbose: 0, quiet: 0, verbosity: Some(level), + no_warnings: false }), level ); } - #[test_case(2, 0, Verbosity::Quiet, tracing_core::LevelFilter::OFF)] - #[test_case(1, 0, Verbosity::Quiet, tracing_core::LevelFilter::OFF)] - #[test_case(0, 0, Verbosity::Normal, tracing_core::LevelFilter::ERROR)] - #[test_case(0, 1, Verbosity::Verbose, tracing_core::LevelFilter::WARN)] - #[test_case(0, 2, Verbosity::Verbose, tracing_core::LevelFilter::INFO)] - #[test_case(0, 3, Verbosity::Verbose, tracing_core::LevelFilter::DEBUG)] - #[test_case(0, 4, Verbosity::Verbose, tracing_core::LevelFilter::TRACE)] - #[test_case(0, 5, Verbosity::Verbose, tracing_core::LevelFilter::TRACE)] + #[test_case(2, 0, false, Verbosity::Quiet, tracing_core::LevelFilter::OFF)] + #[test_case(1, 0, false, Verbosity::Quiet, tracing_core::LevelFilter::OFF)] + #[test_case(0, 0, false, Verbosity::Normal, tracing_core::LevelFilter::ERROR)] + #[test_case(0, 0, true, Verbosity::NoWarnings, tracing_core::LevelFilter::ERROR)] + #[test_case(0, 1, false, Verbosity::Verbose, tracing_core::LevelFilter::WARN)] + #[test_case(0, 2, false, Verbosity::Verbose, tracing_core::LevelFilter::INFO)] + #[test_case(0, 3, false, Verbosity::Verbose, tracing_core::LevelFilter::DEBUG)] + #[test_case(0, 4, false, Verbosity::Verbose, tracing_core::LevelFilter::TRACE)] + #[test_case(0, 5, false, Verbosity::Verbose, tracing_core::LevelFilter::TRACE)] fn verbosity_levels( quiet: u8, verbose: u8, + no_warnings: bool, level: Verbosity, trace: tracing_core::LevelFilter, ) { @@ -123,6 +144,7 @@ mod tests { verbose, quiet, verbosity: None, + no_warnings, }; assert_eq!(spec.as_trace(), format!("scarb={trace}")); assert_eq!(Verbosity::from(spec), level); diff --git a/utils/scarb-ui/src/lib.rs b/utils/scarb-ui/src/lib.rs index 71682fb2e..058225ad5 100644 --- a/utils/scarb-ui/src/lib.rs +++ b/utils/scarb-ui/src/lib.rs @@ -103,7 +103,7 @@ impl Ui { /// Print the message to standard output if not in quiet verbosity mode. pub fn print(&self, message: T) { - if self.verbosity >= Verbosity::Normal { + if self.verbosity > Verbosity::Quiet { self.do_print(message); } } @@ -140,7 +140,9 @@ impl Ui { /// Print a warning to the user. pub fn warn(&self, message: impl AsRef) { - self.print(TypedMessage::styled("warn", "yellow", message.as_ref())) + if self.verbosity > Verbosity::NoWarnings { + self.print(TypedMessage::styled("warn", "yellow", message.as_ref())) + } } /// Print an error to the user. diff --git a/utils/scarb-ui/src/verbosity.rs b/utils/scarb-ui/src/verbosity.rs index c33e138eb..0a723e5ca 100644 --- a/utils/scarb-ui/src/verbosity.rs +++ b/utils/scarb-ui/src/verbosity.rs @@ -14,6 +14,10 @@ pub enum Verbosity { /// /// String representation: `quiet`. Quiet, + /// Avoid printing warnings to standard output. + /// + /// String representation: `no-warnings`. + NoWarnings, /// Default verbosity level. /// /// String representation: `normal`. @@ -29,6 +33,7 @@ impl Display for Verbosity { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Quiet => write!(f, "quiet"), + Self::NoWarnings => write!(f, "no-warnings"), Self::Normal => write!(f, "normal"), Self::Verbose => write!(f, "verbose"), } @@ -41,6 +46,7 @@ impl FromStr for Verbosity { fn from_str(s: &str) -> Result { match s { "quiet" => Ok(Verbosity::Quiet), + "no-warnings" => Ok(Verbosity::NoWarnings), "normal" => Ok(Verbosity::Normal), "verbose" => Ok(Verbosity::Verbose), "" => bail!("empty string cannot be used as verbosity level"), @@ -69,7 +75,8 @@ mod tests { #[test] fn verbosity_ord() { use Verbosity::*; - assert!(Quiet < Normal); + assert!(Quiet < NoWarnings); + assert!(NoWarnings < Normal); assert!(Normal < Verbose); } @@ -77,6 +84,10 @@ mod tests { fn verbosity_from_str() { use Verbosity::*; assert_eq!(Quiet.to_string().parse::().unwrap(), Quiet); + assert_eq!( + NoWarnings.to_string().parse::().unwrap(), + NoWarnings + ); assert_eq!(Normal.to_string().parse::().unwrap(), Normal); assert_eq!(Verbose.to_string().parse::().unwrap(), Verbose); } @@ -88,6 +99,8 @@ mod tests { assert_eq!(Verbosity::from_env_var("SOME_ENV_VAR").unwrap(), Quiet); env::set_var("SOME_ENV_VAR", "verbose"); assert_eq!(Verbosity::from_env_var("SOME_ENV_VAR").unwrap(), Verbose); + env::set_var("SOME_ENV_VAR", "no-warnings"); + assert_eq!(Verbosity::from_env_var("SOME_ENV_VAR").unwrap(), NoWarnings); assert!(Verbosity::from_env_var("SOME_ENV_VAR_THAT_DOESNT_EXIST").is_err()); } }