From 9a7351ab05026be6552e094c543a7509260ea905 Mon Sep 17 00:00:00 2001 From: Matt Solomon Date: Mon, 7 Nov 2022 09:22:49 -0800 Subject: [PATCH] surface `forge fmt` errors, clean up and colorize terminal output (#4) * fix: show warnings/errors from forge fmt * style: better looking log outputs * build: bump version * chore: small cleanup * Added helpful error message (#6) Signed-off-by: Matt Solomon Signed-off-by: Matt Solomon Signed-off-by: Matt Solomon Co-authored-by: John Feras --- Cargo.lock | 14 +++++++++++++- Cargo.toml | 3 ++- README.md | 1 + src/lib.rs | 43 ++++++++++++++++++++++++++++--------------- src/main.rs | 7 ++++--- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d040e3..1024feb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + [[package]] name = "countme" version = "3.0.1" @@ -421,8 +432,9 @@ dependencies = [ [[package]] name = "scopelint" -version = "0.0.10" +version = "0.0.11" dependencies = [ + "colored", "grep", "regex", "taplo", diff --git a/Cargo.toml b/Cargo.toml index 57651e1..be17396 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,10 @@ license = "MIT" name = "scopelint" repository = "https://github.com/ScopeLift/scopelint" - version = "0.0.10" + version = "0.0.11" [dependencies] +colored = "2.0.0" grep = "0.2.10" regex = "1.6.0" taplo = "0.11.0" diff --git a/README.md b/README.md index 301d9e9..9836dc4 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Formatting and checking does the following: - Install with `cargo install scopelint`. - Format code with `scopelint fmt`. - Validate formatting with `scopelint check`. +- Print the version with `scopelint --version`. - Use the ScopeLift [foundry template](https://github.com/ScopeLift/foundry-template/) to automatically run scopelint and slither in CI. ## Limitations diff --git a/src/lib.rs b/src/lib.rs index 90f2743..f5f6d66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![warn(missing_docs, unreachable_pub, unused, rust_2021_compatibility)] #![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)] +use colored::Colorize; use grep::{ matcher::Matcher, regex::RegexMatcher, @@ -39,8 +40,8 @@ impl Config { 2 => match args[1].as_str() { "fmt" => Ok(Self { mode: Mode::Format }), "check" => Ok(Self { mode: Mode::Check }), - "version" => Ok(Self { mode: Mode::Version }), - _ => Err("Unrecognized mode"), + "--version" | "-v" => Ok(Self { mode: Mode::Version }), + _ => Err("Unrecognized mode: Must be 'fmt', 'check', or '--version'"), }, _ => Err("Too many arguments"), } @@ -80,7 +81,12 @@ fn version() { fn fmt(taplo_opts: taplo::formatter::Options) -> Result<(), Box> { // Format Solidity with forge - process::Command::new("forge").arg("fmt").output().expect("forge fmt failed"); + let forge_status = process::Command::new("forge").arg("fmt").output()?; + + // Print any warnings/errors from `forge fmt`. + if !forge_status.stderr.is_empty() { + print!("{}", String::from_utf8(forge_status.stderr)?); + } // Format `foundry.toml` with taplo. let config_orig = fs::read_to_string("./foundry.toml")?; @@ -109,7 +115,11 @@ fn check(taplo_opts: taplo::formatter::Options) -> Result<(), Box> { fn validate_fmt(taplo_opts: taplo::formatter::Options) -> Result<(), Box> { // Check Solidity with `forge fmt` let forge_status = process::Command::new("forge").arg("fmt").arg("--check").output()?; - let forge_ok = forge_status.status.success(); + + // Print any warnings/errors from `forge fmt`. + let stderr = String::from_utf8(forge_status.stderr)?; + let forge_ok = forge_status.status.success() && stderr.is_empty(); + print!("{stderr}"); // Prints nothing if stderr is empty. // Check TOML with `taplo fmt` let config_orig = fs::read_to_string("./foundry.toml")?; @@ -117,7 +127,10 @@ fn validate_fmt(taplo_opts: taplo::formatter::Options) -> Result<(), Box Result<(), Box> { let results = validate(paths)?; if !results.is_valid() { - eprintln!("{results}"); - eprintln!("Error: Naming conventions failed, see details above"); + eprint!("{results}"); + eprintln!("{}: Naming conventions failed, see details above", "error".bold().red()); return Err("Invalid names found".into()) } Ok(()) @@ -153,18 +166,18 @@ impl InvalidItem { fn description(&self) -> String { match self.kind { Validator::Test => { - format!("Invalid test name in {} on line {}: {}\n", self.file, self.line, self.text) + format!("Invalid test name in {} on line {}: {}", self.file, self.line, self.text) } Validator::Constant => { format!( - "Invalid constant or immutable name in {} on line {}: {}\n", + "Invalid constant or immutable name in {} on line {}: {}", self.file, self.line, self.text ) } - Validator::Script => format!("Invalid script interface in {}\n", self.file), + Validator::Script => format!("Invalid script interface in {}", self.file), Validator::Src => { format!( - "Invalid src method name in {} on line {}: {}\n", + "Invalid src method name in {} on line {}: {}", self.file, self.line, self.text ) } @@ -182,16 +195,16 @@ struct ValidationResults { impl fmt::Display for ValidationResults { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { for item in &self.invalid_tests { - write!(f, "{}", item.description())?; + writeln!(f, "{}", item.description())?; } for item in &self.invalid_constants { - write!(f, "{}", item.description())?; + writeln!(f, "{}", item.description())?; } for item in &self.invalid_scripts { - write!(f, "{}", item.description())?; + writeln!(f, "{}", item.description())?; } for item in &self.invalid_src { - write!(f, "{}", item.description())?; + writeln!(f, "{}", item.description())?; } Ok(()) } diff --git a/src/main.rs b/src/main.rs index a048ef1..7a67102 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,19 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs, unreachable_pub, unused, rust_2021_compatibility)] #![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)] +use colored::Colorize; use std::{env, process}; fn main() { let args: Vec = env::args().collect(); let config = scopelint::Config::build(&args).unwrap_or_else(|err| { - eprintln!("Problem parsing arguments: {err}"); + eprintln!("{}: Argument parsing failed with '{err}'", "error".bold().red()); process::exit(1); }); - if let Err(err) = scopelint::run(&config) { - eprintln!("\nExecution failed: {err}"); + if let Err(_err) = scopelint::run(&config) { + // All warnings/errors have already been logged. process::exit(1); } }