use clap::Args;
use std::path::Path;
#[derive(Args)]
pub struct FormatArgs {
#[arg(long)]
pub check: bool,
#[arg(short, long, default_value = ".")]
pub dir: String,
}
pub fn run(args: FormatArgs, _verbose: bool) -> Result<(), Box<dyn std::error::Error>> {
let project_dir = Path::new(&args.dir);
let sources = nativ_pipeline::discover_sources(project_dir)?;
let mut changed = 0usize;
let mut unchanged = 0usize;
let mut errors = 0usize;
for source in &sources {
let content = std::fs::read_to_string(source)?;
match nativ_compiler::format::format_source(&content) {
Ok(formatted) if formatted == content => {
unchanged += 1;
println!("unchanged: {}", source.display());
}
Ok(formatted) => {
changed += 1;
if args.check {
println!("would reformat: {}", source.display());
} else {
std::fs::write(source, formatted)?;
println!("reformatted: {}", source.display());
}
}
Err(e) => {
errors += 1;
eprintln!(" {}: {e}", source.display());
}
}
}
println!(
"{changed} {}, {unchanged} unchanged",
if args.check {
"would be reformatted"
} else {
"reformatted"
}
);
if errors > 0 {
return Err(format!("{errors} file(s) could not be formatted").into());
}
if args.check && changed > 0 {
return Err(format!("{changed} file(s) would be reformatted").into());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn args_for(dir: &Path, check: bool) -> FormatArgs {
FormatArgs {
check,
dir: dir.display().to_string(),
}
}
#[test]
fn fails_when_src_directory_is_missing() {
let tmp = tempfile::tempdir().unwrap();
let err = run(args_for(tmp.path(), false), false).unwrap_err();
assert!(err.to_string().contains("src"), "{err}");
}
#[test]
fn rewrites_messy_file_and_check_then_passes() {
let tmp = tempfile::tempdir().unwrap();
let src_dir = tmp.path().join("src");
std::fs::create_dir_all(&src_dir).unwrap();
let file = src_dir.join("home.nativ");
std::fs::write(&file, "screen Home:\n text \"Hi\" ,red \n").unwrap();
assert!(run(args_for(tmp.path(), true), false).is_err());
assert_eq!(
std::fs::read_to_string(&file).unwrap(),
"screen Home:\n text \"Hi\" ,red \n"
);
assert!(run(args_for(tmp.path(), false), false).is_ok());
assert_eq!(
std::fs::read_to_string(&file).unwrap(),
"screen Home:\n text \"Hi\", red\n"
);
assert!(run(args_for(tmp.path(), true), false).is_ok());
}
#[test]
fn fails_on_unparseable_source() {
let tmp = tempfile::tempdir().unwrap();
let src_dir = tmp.path().join("src");
std::fs::create_dir_all(&src_dir).unwrap();
std::fs::write(src_dir.join("bad.nativ"), "screen Bad:\n\ttext \"tabs\"\n").unwrap();
let err = run(args_for(tmp.path(), false), false).unwrap_err();
assert!(err.to_string().contains("could not be formatted"), "{err}");
}
}