Skip to content

Commit 9a7351a

Browse files
mds1jferas
andauthored
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 <[email protected]> Signed-off-by: Matt Solomon <[email protected]> Signed-off-by: Matt Solomon <[email protected]> Co-authored-by: John Feras <[email protected]>
1 parent bf8c7d4 commit 9a7351a

File tree

5 files changed

+48
-20
lines changed

5 files changed

+48
-20
lines changed

Diff for: Cargo.lock

+13-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
license = "MIT"
77
name = "scopelint"
88
repository = "https://github.com/ScopeLift/scopelint"
9-
version = "0.0.10"
9+
version = "0.0.11"
1010

1111
[dependencies]
12+
colored = "2.0.0"
1213
grep = "0.2.10"
1314
regex = "1.6.0"
1415
taplo = "0.11.0"

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Formatting and checking does the following:
2020
- Install with `cargo install scopelint`.
2121
- Format code with `scopelint fmt`.
2222
- Validate formatting with `scopelint check`.
23+
- Print the version with `scopelint --version`.
2324
- Use the ScopeLift [foundry template](https://github.com/ScopeLift/foundry-template/) to automatically run scopelint and slither in CI.
2425

2526
## Limitations

Diff for: src/lib.rs

+28-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![warn(missing_docs, unreachable_pub, unused, rust_2021_compatibility)]
33
#![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)]
44

5+
use colored::Colorize;
56
use grep::{
67
matcher::Matcher,
78
regex::RegexMatcher,
@@ -39,8 +40,8 @@ impl Config {
3940
2 => match args[1].as_str() {
4041
"fmt" => Ok(Self { mode: Mode::Format }),
4142
"check" => Ok(Self { mode: Mode::Check }),
42-
"version" => Ok(Self { mode: Mode::Version }),
43-
_ => Err("Unrecognized mode"),
43+
"--version" | "-v" => Ok(Self { mode: Mode::Version }),
44+
_ => Err("Unrecognized mode: Must be 'fmt', 'check', or '--version'"),
4445
},
4546
_ => Err("Too many arguments"),
4647
}
@@ -80,7 +81,12 @@ fn version() {
8081

8182
fn fmt(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
8283
// Format Solidity with forge
83-
process::Command::new("forge").arg("fmt").output().expect("forge fmt failed");
84+
let forge_status = process::Command::new("forge").arg("fmt").output()?;
85+
86+
// Print any warnings/errors from `forge fmt`.
87+
if !forge_status.stderr.is_empty() {
88+
print!("{}", String::from_utf8(forge_status.stderr)?);
89+
}
8490

8591
// Format `foundry.toml` with taplo.
8692
let config_orig = fs::read_to_string("./foundry.toml")?;
@@ -109,15 +115,22 @@ fn check(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
109115
fn validate_fmt(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
110116
// Check Solidity with `forge fmt`
111117
let forge_status = process::Command::new("forge").arg("fmt").arg("--check").output()?;
112-
let forge_ok = forge_status.status.success();
118+
119+
// Print any warnings/errors from `forge fmt`.
120+
let stderr = String::from_utf8(forge_status.stderr)?;
121+
let forge_ok = forge_status.status.success() && stderr.is_empty();
122+
print!("{stderr}"); // Prints nothing if stderr is empty.
113123

114124
// Check TOML with `taplo fmt`
115125
let config_orig = fs::read_to_string("./foundry.toml")?;
116126
let config_fmt = taplo::formatter::format(&config_orig, taplo_opts);
117127
let taplo_ok = config_orig == config_fmt;
118128

119129
if !forge_ok || !taplo_ok {
120-
eprintln!("Error: Formatting failed, run `scopelint fmt` to fix");
130+
eprintln!(
131+
"{}: Formatting validation failed, run `scopelint fmt` to fix",
132+
"error".bold().red()
133+
);
121134
return Err("Invalid fmt found".into())
122135
}
123136
Ok(())
@@ -128,8 +141,8 @@ fn validate_names() -> Result<(), Box<dyn Error>> {
128141
let results = validate(paths)?;
129142

130143
if !results.is_valid() {
131-
eprintln!("{results}");
132-
eprintln!("Error: Naming conventions failed, see details above");
144+
eprint!("{results}");
145+
eprintln!("{}: Naming conventions failed, see details above", "error".bold().red());
133146
return Err("Invalid names found".into())
134147
}
135148
Ok(())
@@ -153,18 +166,18 @@ impl InvalidItem {
153166
fn description(&self) -> String {
154167
match self.kind {
155168
Validator::Test => {
156-
format!("Invalid test name in {} on line {}: {}\n", self.file, self.line, self.text)
169+
format!("Invalid test name in {} on line {}: {}", self.file, self.line, self.text)
157170
}
158171
Validator::Constant => {
159172
format!(
160-
"Invalid constant or immutable name in {} on line {}: {}\n",
173+
"Invalid constant or immutable name in {} on line {}: {}",
161174
self.file, self.line, self.text
162175
)
163176
}
164-
Validator::Script => format!("Invalid script interface in {}\n", self.file),
177+
Validator::Script => format!("Invalid script interface in {}", self.file),
165178
Validator::Src => {
166179
format!(
167-
"Invalid src method name in {} on line {}: {}\n",
180+
"Invalid src method name in {} on line {}: {}",
168181
self.file, self.line, self.text
169182
)
170183
}
@@ -182,16 +195,16 @@ struct ValidationResults {
182195
impl fmt::Display for ValidationResults {
183196
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
184197
for item in &self.invalid_tests {
185-
write!(f, "{}", item.description())?;
198+
writeln!(f, "{}", item.description())?;
186199
}
187200
for item in &self.invalid_constants {
188-
write!(f, "{}", item.description())?;
201+
writeln!(f, "{}", item.description())?;
189202
}
190203
for item in &self.invalid_scripts {
191-
write!(f, "{}", item.description())?;
204+
writeln!(f, "{}", item.description())?;
192205
}
193206
for item in &self.invalid_src {
194-
write!(f, "{}", item.description())?;
207+
writeln!(f, "{}", item.description())?;
195208
}
196209
Ok(())
197210
}

Diff for: src/main.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#![doc = include_str!("../README.md")]
22
#![warn(missing_docs, unreachable_pub, unused, rust_2021_compatibility)]
33
#![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)]
4+
use colored::Colorize;
45
use std::{env, process};
56

67
fn main() {
78
let args: Vec<String> = env::args().collect();
89

910
let config = scopelint::Config::build(&args).unwrap_or_else(|err| {
10-
eprintln!("Problem parsing arguments: {err}");
11+
eprintln!("{}: Argument parsing failed with '{err}'", "error".bold().red());
1112
process::exit(1);
1213
});
1314

14-
if let Err(err) = scopelint::run(&config) {
15-
eprintln!("\nExecution failed: {err}");
15+
if let Err(_err) = scopelint::run(&config) {
16+
// All warnings/errors have already been logged.
1617
process::exit(1);
1718
}
1819
}

0 commit comments

Comments
 (0)