use std::fs;
use std::path::Path;
use anyhow::Result;
use crate::hooks::{is_executable, MANAGED_BLOCK_BEGIN};
pub fn print_status(repo_root: &Path, git_dir: &Path, verbose: bool) -> Result<()> {
let hooks_dir = git_dir.join("hooks");
println!("Repository: {}", repo_root.display());
println!("Git dir: {}", git_dir.display());
println!("Hooks dir: {}", hooks_dir.display());
if !hooks_dir.is_dir() {
println!("Hooks dir status: missing");
println!("pre-commit: not installed");
return Ok(());
}
inspect_pre_commit(&hooks_dir, verbose)?;
Ok(())
}
fn inspect_pre_commit(hooks_dir: &Path, verbose: bool) -> Result<()> {
let hook_path = hooks_dir.join("pre-commit");
if !hook_path.exists() {
println!("pre-commit: not installed");
print_hook_backups(hooks_dir, "pre-commit")?;
return Ok(());
}
println!("pre-commit: installed");
if let Some(is_executable) = is_executable(&hook_path) {
println!("pre-commit executable: {is_executable}");
}
let Ok(contents) = fs::read_to_string(&hook_path) else {
println!("pre-commit readable: false");
print_hook_backups(hooks_dir, "pre-commit")?;
return Ok(());
};
println!("pre-commit readable: true");
let has_managed_block = contents
.lines()
.any(|line| line.trim() == MANAGED_BLOCK_BEGIN);
println!("pre-commit has git-hook-installer managed block: {has_managed_block}");
let looks_like_cargo_fmt = contents.lines().any(|line| line.trim() == "cargo fmt");
println!("pre-commit runs cargo fmt: {looks_like_cargo_fmt}");
if let Some(cd_dir) = parse_cd_dir(&contents) {
println!("pre-commit cd: {cd_dir}");
}
if verbose {
print_hook_summary(&contents);
}
print_hook_backups(hooks_dir, "pre-commit")?;
Ok(())
}
fn parse_cd_dir(contents: &str) -> Option<String> {
for line in contents.lines() {
let line = line.trim();
if !line.starts_with("cd ") {
continue;
}
let raw = line.trim_start_matches("cd ").trim();
let unquoted = raw
.strip_prefix('"')
.and_then(|rest| rest.strip_suffix('"'))
.unwrap_or(raw);
return Some(unquoted.to_string());
}
None
}
fn print_hook_summary(contents: &str) {
let line_count = contents.lines().count();
println!("pre-commit lines: {line_count}");
let has_shebang = contents
.lines()
.next()
.is_some_and(|line| line.starts_with("#!"));
println!("pre-commit has shebang: {has_shebang}");
}
fn print_hook_backups(hooks_dir: &Path, hook_file_name: &str) -> Result<()> {
let entries = match fs::read_dir(hooks_dir) {
Ok(entries) => entries,
Err(_) => return Ok(()),
};
let prefix = format!("{hook_file_name}.bak");
let mut backups = Vec::new();
for entry in entries {
let entry = match entry {
Ok(entry) => entry,
Err(_) => continue,
};
let file_name = entry.file_name();
let Some(file_name) = file_name.to_str() else {
continue;
};
if !file_name.starts_with(&prefix) {
continue;
}
backups.push(file_name.to_string());
}
backups.sort();
if backups.is_empty() {
return Ok(());
}
println!("pre-commit backups: {}", backups.join(", "));
Ok(())
}