use std::path::PathBuf;
use std::process::Command;
use anyhow::{anyhow, Context, Result};
use clap::Args;
mod utils;
use utils::*;
#[derive(Args)]
pub struct FormatArgs {
#[arg(long)]
pub dry_run: bool,
#[arg(short, long)]
pub commit: bool,
pub files: Vec<PathBuf>,
#[arg(short, long)]
pub verbose: bool,
}
pub async fn execute(format_args: &FormatArgs) -> Result<()> {
let git_root = find_git_root()
.context("Failed to find git repository root. Make sure you're in a git repository.")?;
let editorconfig_path = git_root.join(".editorconfig");
if !editorconfig_path.exists() {
return Err(anyhow!(
".editorconfig file not found in git root: {}",
git_root.display()
));
}
if format_args.verbose {
println!("Found .editorconfig at: {}", editorconfig_path.display());
println!("Git root: {}", git_root.display());
}
let files_to_format = if format_args.files.is_empty() {
get_git_files(&git_root)?
} else {
format_args
.files
.iter()
.map(|f| {
if f.is_absolute() {
f.clone()
} else {
git_root.join(f)
}
})
.filter(|f| f.exists())
.collect()
};
if files_to_format.is_empty() {
println!("No files to format found.");
return Ok(());
}
if format_args.verbose {
println!("Found {} files to process", files_to_format.len());
}
let mut formatted_files = Vec::new();
let mut total_changes = 0;
for file_path in &files_to_format {
if format_args.verbose {
println!("Processing: {}", file_path.display());
}
let changes = format_file(file_path, !format_args.dry_run, format_args.verbose)
.with_context(|| format!("Failed to format file: {}", file_path.display()))?;
if changes > 0 {
formatted_files.push(file_path.clone());
total_changes += changes;
}
}
if total_changes == 0 {
println!("No formatting changes needed.");
return Ok(());
}
let action = if format_args.dry_run {
"would be applied"
} else {
"applied"
};
println!(
"Formatting complete: {} changes across {} files ({})",
total_changes,
formatted_files.len(),
action
);
if !format_args.dry_run && format_args.commit {
commit_changes(&formatted_files)?;
}
Ok(())
}
fn commit_changes(files: &[PathBuf]) -> Result<()> {
for file in files {
let output = Command::new("git")
.args(["add", &file.to_string_lossy()])
.output()
.context("Failed to execute git add command")?;
if !output.status.success() {
return Err(anyhow!(
"Git add failed for {}: {}",
file.display(),
String::from_utf8_lossy(&output.stderr)
));
}
}
let commit_message = "Format code according to .editorconfig";
let output = Command::new("git")
.args(["commit", "-m", commit_message])
.output()
.context("Failed to execute git commit command")?;
if !output.status.success() {
return Err(anyhow!(
"Git commit failed: {}",
String::from_utf8_lossy(&output.stderr)
));
}
println!("Successfully committed formatting changes:");
println!(" Message: {}", commit_message);
println!(" Files: {}", files.len());
Ok(())
}