Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow hiding warnings in Scarb output #1689

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scarb/src/sources/git/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
16 changes: 16 additions & 0 deletions scarb/tests/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ fn dry_run() {
.run();
}

#[test]
fn dry_run_without_warnings() {
ManifestEditHarness::offline()
.args(["add", "--dry-run", "--no-warnings", "[email protected]"])
.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();
Expand Down
64 changes: 45 additions & 19 deletions utils/scarb-ui/src/args/verbosity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -35,24 +44,26 @@ 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,
}
}
}

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,
Expand All @@ -63,21 +74,31 @@ impl VerbositySpec {
}

fn integer_verbosity(&self) -> i8 {
let int_level = (self.verbose as i8) - (self.quiet as i8);
if self.is_present() {
int_level
if self.no_warnings {
-1
} else {
self.verbosity
.map(Verbosity::level_value)
.unwrap_or(int_level)
let int_level = if self.no_warnings {
-1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check twice? 🤔
Can we do

if self.no_warnings {
    return -1;
}

at the beginning of this function, and avoid changing anything else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I initially added the check at the beginning of the function, but later thought setting int_level first is just more in line with existing approach. Removed the extra check 👍🏻

} else {
(self.verbose as i8) - (self.quiet as i8 * 2)
};

if self.is_present() {
int_level
} else {
self.verbosity
.map(Verbosity::level_value)
.unwrap_or(int_level)
}
}
}
}

impl From<VerbositySpec> 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,
}
Expand All @@ -92,6 +113,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) {
Expand All @@ -100,29 +122,33 @@ 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)]
Comment on lines +128 to +129
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we do not need to change this tests, if we do:

impl From<VerbositySpec> for Verbosity {
    fn from(spec: VerbositySpec) -> Self {
        match spec.integer_verbosity() {
            v if v < -1 => Verbosity::Quiet,
            -1 => Verbosity::NoWarnings,

Shouldn't quiet=1, verbose=0 be the new one then? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand, quiet=1, verbose=0 shouldn't produce any different verbosity level than before? That's not something this PR aims to change 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you are changing impl From<VerbositySpec> for Verbosity which is used to parse it, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am, but only to create conditions for setting NoWarnings verbosity level, without affecting impact of --quiet and --verbose flags on settings their respectful verbosity level.

#[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,
) {
let spec = VerbositySpec {
verbose,
quiet,
verbosity: None,
no_warnings,
};
assert_eq!(spec.as_trace(), format!("scarb={trace}"));
assert_eq!(Verbosity::from(spec), level);
Expand Down
4 changes: 3 additions & 1 deletion utils/scarb-ui/src/lib.rs
DelevoXDG marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ impl Ui {

/// Print a warning to the user.
pub fn warn(&self, message: impl AsRef<str>) {
self.print(TypedMessage::styled("warn", "yellow", message.as_ref()))
if self.verbosity > Verbosity::NoWarnings {
maciektr marked this conversation as resolved.
Show resolved Hide resolved
self.print(TypedMessage::styled("warn", "yellow", message.as_ref()))
}
}

/// Print an error to the user.
Expand Down
15 changes: 14 additions & 1 deletion utils/scarb-ui/src/verbosity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub enum Verbosity {
///
/// String representation: `quiet`.
Quiet,
/// Avoid printing warnings to standard output.
///
/// String representation: `no-warnings`.
NoWarnings,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be

Suggested change
NoWarnings,
Error,

Indicating that it will print errors but not warnings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my initial thought too, but we have some outputs that aren’t warnings or errors, like:

    ws.config()ui().print(Status::new("Packaging", &pkg_id.to_string()));

I figured we probably want to keep these messages and only mute warnings, so NoWarnings seemed more fitting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I would still use ERROR as it's more customary

Copy link
Contributor Author

@DelevoXDG DelevoXDG Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure it's more customary? It might make sense as environment variable setting, but, unlike --quiet, --verbose and --no-warnings, --error as a flag imo doesn't seem like it is directly related to verbosity, and reading docs will be required to understand what it actually does.

/// Default verbosity level.
///
/// String representation: `normal`.
Expand All @@ -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"),
}
Expand All @@ -41,6 +46,7 @@ impl FromStr for Verbosity {
fn from_str(s: &str) -> Result<Self> {
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"),
Expand Down Expand Up @@ -69,14 +75,19 @@ mod tests {
#[test]
fn verbosity_ord() {
use Verbosity::*;
assert!(Quiet < Normal);
assert!(Quiet < NoWarnings);
assert!(NoWarnings < Normal);
assert!(Normal < Verbose);
}

#[test]
fn verbosity_from_str() {
use Verbosity::*;
assert_eq!(Quiet.to_string().parse::<Verbosity>().unwrap(), Quiet);
assert_eq!(
NoWarnings.to_string().parse::<Verbosity>().unwrap(),
NoWarnings
);
assert_eq!(Normal.to_string().parse::<Verbosity>().unwrap(), Normal);
assert_eq!(Verbose.to_string().parse::<Verbosity>().unwrap(), Verbose);
}
Expand All @@ -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());
}
}
Loading