diff --git a/cli/src/format.rs b/cli/src/format.rs index 8ced266bce..f5a32c9caa 100644 --- a/cli/src/format.rs +++ b/cli/src/format.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use crate::FormatMode; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_error_macros::{internal_error, user_error}; @@ -24,7 +25,7 @@ use roc_parse::{ }; use roc_region::all::{Loc, Region}; -pub fn format(files: std::vec::Vec) { +pub fn format(files: std::vec::Vec, mode: FormatMode) -> Result<(), String> { for file in files { let arena = Bump::new(); @@ -99,9 +100,22 @@ pub fn format(files: std::vec::Vec) { unstable_2_file.display()); } - // If all the checks above passed, actually write out the new file. - std::fs::write(&file, buf.as_str()).unwrap(); + match mode { + FormatMode::CheckOnly => { + // If we notice that this file needs to be formatted, return early + if buf.as_str() != src { + return Err("One or more files need to be reformatted.".to_string()); + } + } + + FormatMode::Format => { + // If all the checks above passed, actually write out the new file. + std::fs::write(&file, buf.as_str()).unwrap(); + } + } } + + Ok(()) } #[derive(Debug, PartialEq)] diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 136f99eab7..e3027927ea 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -37,6 +37,7 @@ pub const FLAG_TIME: &str = "time"; pub const FLAG_LINK: &str = "roc-linker"; pub const FLAG_PRECOMPILED: &str = "precompiled-host"; pub const FLAG_VALGRIND: &str = "valgrind"; +pub const FLAG_CHECK: &str = "check"; pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_DIR: &str = "ROC_DIR"; pub const BACKEND: &str = "BACKEND"; @@ -122,6 +123,12 @@ pub fn build_app<'a>() -> App<'a> { .index(1) .multiple_values(true) .required(false)) + .arg( + Arg::new(FLAG_CHECK) + .long(FLAG_CHECK) + .about("Checks that specified files are formatted. If formatting is needed, it will return a non-zero exit code.") + .required(false), + ) ) .subcommand(App::new(CMD_VERSION) .about("Print version information") @@ -242,6 +249,11 @@ pub enum BuildConfig { BuildAndRun { roc_file_arg_index: usize }, } +pub enum FormatMode { + Format, + CheckOnly, +} + pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { use build::build_file; use std::str::FromStr; diff --git a/cli/src/main.rs b/cli/src/main.rs index 7fba8781c5..a90ca61554 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,7 +1,7 @@ use roc_cli::build::check_file; use roc_cli::{ - build_app, docs, format, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, CMD_FORMAT, - CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE, + build_app, docs, format, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, + CMD_FORMAT, CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_TIME, ROC_FILE, }; use roc_load::file::LoadingProblem; use std::fs::{self, FileType}; @@ -150,9 +150,20 @@ fn main() -> io::Result<()> { roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?; } - format(roc_files); + let format_mode = match matches.is_present(FLAG_CHECK) { + true => FormatMode::CheckOnly, + false => FormatMode::Format, + }; - Ok(0) + let format_exit_code = match format(roc_files, format_mode) { + Ok(_) => 0, + Err(message) => { + eprintln!("{}", message); + 1 + } + }; + + Ok(format_exit_code) } Some((CMD_VERSION, _)) => { println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n"));