Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ This command will format:
- Solidity files using the configuration specified in `foundry.toml`.
- TOML files using a hardcoded configuration that indents keys and sorts them alphabetically to improve readability.

**Flags:**
- `--check`: Show changes without modifying files (dry run mode)

### `scopelint check`

This command ensures that development [best practices](https://book.getfoundry.sh/tutorials/best-practices) are consistently followed by validating that:
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ pub enum Subcommands {
Check,
#[clap(about = "Formats Solidity and TOML files in the codebase.")]
/// Formats Solidity and TOML files in the codebase.
Fmt,
Fmt {
#[clap(long, help = "Show changes without modifying files")]
/// Show changes without modifying files.
check: bool,
},
#[clap(about = "Generates a specification for the current project from test names.")]
/// Generates a specification for the current project from test names.
Spec,
Expand Down
106 changes: 103 additions & 3 deletions src/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,99 @@
use colored::Colorize;
use std::{error::Error, fs, process};

/// Format the code.
/// Check formatting without modifying files.
/// # Errors
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
fn check_formatting(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
println!("Checking formatting...");

let forge_status = process::Command::new("forge").args(["fmt", "--check"]).output()?;

let mut has_changes = false;

// Print any warnings/errors from `forge fmt --check`.
if !forge_status.stderr.is_empty() {
print!("{}", String::from_utf8(forge_status.stderr)?);
}

// Print the diff output from forge fmt --check with colors
if !forge_status.stdout.is_empty() {
println!("Solidity files that would be reformatted:");
let forge_output = String::from_utf8(forge_status.stdout)?;

for line in forge_output.lines() {
if line.starts_with("Diff in ") {
println!("{line}");
} else if line.contains("|-") {
// Red for removed lines
let parts: Vec<&str> = line.split("|-").collect();
if parts.len() == 2 {
println!("{}{}{}", parts[0], "|-".red(), parts[1].red());
} else {
println!("{line}");
}
} else if line.contains("|+") {
// Green for added lines
let parts: Vec<&str> = line.split("|+").collect();
if parts.len() == 2 {
println!("{}{}{}", parts[0], "|+".green(), parts[1].green());
} else {
println!("{line}");
}
} else {
println!("{line}");
}
}
has_changes = true;
}

// Check if forge fmt found any issues
if !forge_status.status.success() {
has_changes = true;
}

// Check foundry.toml formatting
let config_orig = fs::read_to_string("./foundry.toml")?;
let config_fmt = taplo::formatter::format(&config_orig, taplo_opts);

if config_orig != config_fmt {
println!("foundry.toml would be reformatted:");
println!("Diff in foundry.toml:");

// Simple diff output with colors
let orig_lines: Vec<&str> = config_orig.lines().collect();
let fmt_lines: Vec<&str> = config_fmt.lines().collect();

for (i, line) in fmt_lines.iter().enumerate() {
if i < orig_lines.len() && orig_lines[i] != *line {
// Red for removed lines
println!("{} |{}{}", i + 1, "-".red(), orig_lines[i].red());
// Green for added lines
println!("{} |{}{}", i + 1, "+".green(), line.green());
} else if i >= orig_lines.len() {
// Green for new lines
println!("{} |{}{}", i + 1, "+".green(), line.green());
}
}

has_changes = true;
}

// Exit with error code if any files would be changed
if has_changes {
println!("\nRun 'scopelint fmt' to apply these changes.");
process::exit(1);
} else {
println!("All files are properly formatted!");
}

Ok(())
}

/// Apply formatting to files.
/// # Errors
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
pub fn run(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
// Format Solidity with forge
fn apply_formatting(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>> {
let forge_status = process::Command::new("forge").arg("fmt").output()?;

// Print any warnings/errors from `forge fmt`.
Expand All @@ -18,3 +107,14 @@ pub fn run(taplo_opts: taplo::formatter::Options) -> Result<(), Box<dyn Error>>
fs::write("./foundry.toml", config_fmt)?;
Ok(())
}

/// Format the code.
/// # Errors
/// Errors if `forge fmt` fails, or if `taplo` fails to format `foundry.toml`.
pub fn run(taplo_opts: taplo::formatter::Options, check: bool) -> Result<(), Box<dyn Error>> {
if check {
check_formatting(taplo_opts)
} else {
apply_formatting(taplo_opts)
}
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ pub fn run(opts: &config::Opts) -> Result<(), Box<dyn Error>> {
};

// Execute commands.
match opts.subcommand {
match &opts.subcommand {
config::Subcommands::Check => check::run(taplo_opts),
config::Subcommands::Fmt => fmt::run(taplo_opts),
config::Subcommands::Fmt { check } => fmt::run(taplo_opts, *check),
config::Subcommands::Spec => spec::run(),
}
}
Loading