Skip to content

Commit c9f997c

Browse files
committed
feat: add scopelint fmt --check
1 parent a1441eb commit c9f997c

File tree

4 files changed

+113
-6
lines changed

4 files changed

+113
-6
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ This command will format:
3333
- Solidity files using the configuration specified in `foundry.toml`.
3434
- TOML files using a hardcoded configuration that indents keys and sorts them alphabetically to improve readability.
3535

36+
**Flags:**
37+
- `--check`: Show changes without modifying files (dry run mode)
38+
3639
### `scopelint check`
3740

3841
This command ensures that development [best practices](https://book.getfoundry.sh/tutorials/best-practices) are consistently followed by validating that:

src/config.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ pub enum Subcommands {
1717
Check,
1818
#[clap(about = "Formats Solidity and TOML files in the codebase.")]
1919
/// Formats Solidity and TOML files in the codebase.
20-
Fmt,
20+
Fmt {
21+
#[clap(long, help = "Show changes without modifying files")]
22+
/// Show changes without modifying files.
23+
check: bool,
24+
},
2125
#[clap(about = "Generates a specification for the current project from test names.")]
2226
/// Generates a specification for the current project from test names.
2327
Spec,

src/fmt/mod.rs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,99 @@
11
use std::{error::Error, fs, process};
2+
use colored::*;
23

3-
/// Format the code.
4+
/// Check formatting without modifying files.
45
/// # Errors
56
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
6-
pub fn run(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
7-
// Format Solidity with forge
7+
fn check_formatting(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
8+
println!("Checking formatting...");
9+
10+
let forge_status = process::Command::new("forge").args(["fmt", "--check"]).output()?;
11+
12+
let mut has_changes = false;
13+
14+
// Print any warnings/errors from `forge fmt --check`.
15+
if !forge_status.stderr.is_empty() {
16+
print!("{}", String::from_utf8(forge_status.stderr)?);
17+
}
18+
19+
// Print the diff output from forge fmt --check with colors
20+
if !forge_status.stdout.is_empty() {
21+
println!("Solidity files that would be reformatted:");
22+
let forge_output = String::from_utf8(forge_status.stdout)?;
23+
24+
for line in forge_output.lines() {
25+
if line.starts_with("Diff in ") {
26+
println!("{}", line);
27+
} else if line.contains("|-") {
28+
// Red for removed lines
29+
let parts: Vec<&str> = line.split("|-").collect();
30+
if parts.len() == 2 {
31+
println!("{}{}{}", parts[0], "|-".red(), parts[1].red());
32+
} else {
33+
println!("{}", line);
34+
}
35+
} else if line.contains("|+") {
36+
// Green for added lines
37+
let parts: Vec<&str> = line.split("|+").collect();
38+
if parts.len() == 2 {
39+
println!("{}{}{}", parts[0], "|+".green(), parts[1].green());
40+
} else {
41+
println!("{}", line);
42+
}
43+
} else {
44+
println!("{}", line);
45+
}
46+
}
47+
has_changes = true;
48+
}
49+
50+
// Check if forge fmt found any issues
51+
if !forge_status.status.success() {
52+
has_changes = true;
53+
}
54+
55+
// Check foundry.toml formatting
56+
let config_orig = fs::read_to_string("./foundry.toml")?;
57+
let config_fmt = taplo::formatter::format(&config_orig, taplo_opts);
58+
59+
if config_orig != config_fmt {
60+
println!("foundry.toml would be reformatted:");
61+
println!("Diff in foundry.toml:");
62+
63+
// Simple diff output with colors
64+
let orig_lines: Vec<&str> = config_orig.lines().collect();
65+
let fmt_lines: Vec<&str> = config_fmt.lines().collect();
66+
67+
for (i, line) in fmt_lines.iter().enumerate() {
68+
if i < orig_lines.len() && orig_lines[i] != *line {
69+
// Red for removed lines
70+
println!("{} |{}{}", i + 1, "-".red(), orig_lines[i].red());
71+
// Green for added lines
72+
println!("{} |{}{}", i + 1, "+".green(), line.green());
73+
} else if i >= orig_lines.len() {
74+
// Green for new lines
75+
println!("{} |{}{}", i + 1, "+".green(), line.green());
76+
}
77+
}
78+
79+
has_changes = true;
80+
}
81+
82+
// Exit with error code if any files would be changed
83+
if has_changes {
84+
println!("\nRun 'scopelint fmt' to apply these changes.");
85+
process::exit(1);
86+
} else {
87+
println!("All files are properly formatted!");
88+
}
89+
90+
Ok(())
91+
}
92+
93+
/// Apply formatting to files.
94+
/// # Errors
95+
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
96+
fn apply_formatting(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
897
let forge_status = process::Command::new("forge").arg("fmt").output()?;
998

1099
// Print any warnings/errors from `forge fmt`.
@@ -18,3 +107,14 @@ pub fn run(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>>
18107
fs::write("./foundry.toml", config_fmt)?;
19108
Ok(())
20109
}
110+
111+
/// Format the code.
112+
/// # Errors
113+
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
114+
pub fn run(taplo_opts: taplo::formatter::Options, check: bool) -> Result<(), Box<dyn Error>> {
115+
if check {
116+
check_formatting(taplo_opts)
117+
} else {
118+
apply_formatting(taplo_opts)
119+
}
120+
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ pub fn run(opts: &config::Opts) -> Result<(), Box<dyn Error>> {
3333
};
3434

3535
// Execute commands.
36-
match opts.subcommand {
36+
match &opts.subcommand {
3737
config::Subcommands::Check => check::run(taplo_opts),
38-
config::Subcommands::Fmt => fmt::run(taplo_opts),
38+
config::Subcommands::Fmt { check } => fmt::run(taplo_opts, *check),
3939
config::Subcommands::Spec => spec::run(),
4040
}
4141
}

0 commit comments

Comments
 (0)