diff --git a/Cargo.lock b/Cargo.lock index 60b032210b0..9536508436b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2436,6 +2436,7 @@ dependencies = [ "rustc_version", "serde", "serde_json", + "similar-asserts", "tempfile", "termcolor", "test-binary", diff --git a/Cargo.toml b/Cargo.toml index 11a07a0fb76..b891aa7d935 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ hex = "0.4.2" const_format = "0.2.30" num-bigint = "0.4" num-traits = "0.2" +similar-asserts = "1.5.0" [profile.dev] # This is required to be able to run `cargo test` in acvm_js due to the `locals exceeds maximum` error. diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index 1a08514dc5f..07298ae25d2 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -43,6 +43,7 @@ tower.workspace = true async-lsp = { workspace = true, features = ["client-monitor", "stdio", "tracing", "tokio"] } const_format.workspace = true hex.workspace = true +similar-asserts.workspace = true termcolor = "1.1.2" color-eyre = "0.6.2" tokio = { version = "1.0", features = ["io-std"] } diff --git a/tooling/nargo_cli/src/cli/fmt_cmd.rs b/tooling/nargo_cli/src/cli/fmt_cmd.rs index 638eaa64b3e..ec3d373a483 100644 --- a/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -13,9 +13,15 @@ use super::NargoConfig; /// Format the Noir files in a workspace #[derive(Debug, Clone, Args)] -pub(crate) struct FormatCommand {} +pub(crate) struct FormatCommand { + /// Run noirfmt in check mode + #[arg(long)] + check: bool, +} + +pub(crate) fn run(args: FormatCommand, config: NargoConfig) -> Result<(), CliError> { + let check_mode = args.check; -pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliError> { let toml_path = get_package_manifest(&config.program_dir)?; let workspace = resolve_workspace_from_toml( &toml_path, @@ -26,6 +32,8 @@ pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliEr let config = nargo_fmt::Config::read(&config.program_dir) .map_err(|err| CliError::Generic(err.to_string()))?; + let mut check_exit_code_one = false; + for package in &workspace { let mut file_manager = FileManager::new(&package.root_dir, Box::new(|path| std::fs::read_to_string(path))); @@ -53,16 +61,40 @@ pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliEr return Ok(()); } - let source = nargo_fmt::format( - file_manager.fetch_file(file_id).source(), - parsed_module, - &config, - ); + let original = file_manager.fetch_file(file_id).source(); + let formatted = nargo_fmt::format(original, parsed_module, &config); + + if check_mode { + let diff = similar_asserts::SimpleDiff::from_str( + original, + &formatted, + "original", + "formatted", + ) + .to_string(); + + if !diff.lines().next().is_some_and(|line| line.contains("Invisible differences")) { + if !check_exit_code_one { + check_exit_code_one = true; + } - std::fs::write(entry.path(), source) + println!("{diff}"); + } + + Ok(()) + } else { + std::fs::write(entry.path(), formatted) + } }) .map_err(|error| CliError::Generic(error.to_string()))?; } + + if check_exit_code_one { + std::process::exit(1); + } else if check_mode { + println!("No formatting changes were detected"); + } + Ok(()) } diff --git a/tooling/nargo_fmt/Cargo.toml b/tooling/nargo_fmt/Cargo.toml index 921c9893ab5..374413ac9f2 100644 --- a/tooling/nargo_fmt/Cargo.toml +++ b/tooling/nargo_fmt/Cargo.toml @@ -13,4 +13,4 @@ toml.workspace = true thiserror.workspace = true [dev-dependencies] -similar-asserts = "1.5.0" +similar-asserts.workspace = true