mod git;
mod hook;
mod output;
mod validation;
use std::process;
use color_eyre::eyre::{Context, Result, eyre};
fn main() -> Result<()> {
color_eyre::install()?;
output::enable_ansi_support();
let args: Vec<String> = std::env::args().collect();
match args.get(1).map(|s| s.as_str()) {
Some("install") => cmd_install(),
Some("validate") => {
let file = args
.get(2)
.ok_or_else(|| eyre!("uso: cc-hook validate <arquivo>"))?;
cmd_validate(file)
}
_ => {
print_usage();
Ok(())
}
}
}
fn print_usage() {
println!("cc-hook - Conventional Commits Hook");
println!();
println!("Uso:");
println!(" cc-hook install Instala o hook commit-msg no repositório atual");
println!(" cc-hook validate <arquivo> Valida a mensagem de commit no arquivo");
println!();
println!("Exemplos:");
println!(" cc-hook install");
println!(" cc-hook validate .git/COMMIT_EDITMSG");
}
fn cmd_install() -> Result<()> {
let current_dir =
std::env::current_dir().wrap_err("não foi possível obter o diretório atual")?;
let git_root = match git::find_git_root(¤t_dir) {
Some(root) => root,
None => {
output::print_error(
"Este diretório não é um repositório Git (não existe a pasta .git).",
);
println!();
println!("Para inicializar um repositório Git, execute:");
println!(" git init");
process::exit(1);
}
};
let hook_file = hook::hook_path(&git_root);
if hook_file.exists() {
output::print_warning("Já existe um hook commit-msg.");
if !prompt_overwrite()? {
output::print_info("Operação cancelada.");
return Ok(());
}
}
let backup = hook::install_hook(&git_root)
.wrap_err("falha ao instalar o hook")?;
if let Some(backup_path) = backup {
output::print_info(&format!(
"Backup criado: {}",
backup_path.display()
));
}
output::print_success("Hook commit-msg configurado com sucesso!");
println!();
output::print_info(&format!(
"O hook foi instalado em: {}",
hook_file.display()
));
println!();
println!("🎯 A partir de agora, todos os commits deste repositório serão validados");
println!(" automaticamente para seguir o padrão Conventional Commits.");
println!();
println!("💡 Para testar o hook, faça um commit:");
println!(" git add .");
println!(" git commit -m \"feat: adicionar nova funcionalidade\"");
println!();
println!("🔧 Para remover o hook, execute:");
println!(" rm {}", hook_file.display());
Ok(())
}
fn prompt_overwrite() -> Result<bool> {
use std::io::{self, BufRead, Write};
print!("Deseja sobrescrever? (y/N): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().lock().read_line(&mut input)?;
Ok(input.trim().eq_ignore_ascii_case("y"))
}
fn cmd_validate(file: &str) -> Result<()> {
let content = std::fs::read_to_string(file)
.wrap_err_with(|| format!("não foi possível ler o arquivo: {file}"))?;
match validation::validate_commit_message(&content) {
validation::ValidationResult::Valid | validation::ValidationResult::Skip => {
process::exit(0);
}
validation::ValidationResult::EmptyMessage => {
output::print_error("Mensagem de commit vazia.");
process::exit(1);
}
validation::ValidationResult::Invalid => {
let first_line = content.lines().next().unwrap_or("").trim();
output::print_validation_error(first_line);
process::exit(1);
}
}
}