cargo-setupx 0.1.0

Rust-based CLI and library that automates the initial setup of new Rust projects with modular configuration packs
Documentation
//! Hooks pack - sets up Git hooks for automated quality checks

use crate::error::Result;
use crate::templates;
use crate::utils::{create_dir, make_executable, write_file};
use std::path::Path;

/// Apply the hooks pack to the project
pub fn apply(project_path: &Path, force: bool) -> Result<()> {
    println!("🪝 Applying Hooks Pack...");

    // Create .githooks directory
    let githooks_dir = project_path.join(".githooks");
    create_dir(&githooks_dir)?;

    // Create pre-push hook
    let pre_push_path = githooks_dir.join("pre-push");
    write_file(&pre_push_path, templates::PRE_PUSH_HOOK, force)?;
    make_executable(&pre_push_path)?;

    // Create setup.sh script
    let setup_sh_path = githooks_dir.join("setup.sh");
    write_file(&setup_sh_path, templates::SETUP_HOOKS_SH, force)?;
    make_executable(&setup_sh_path)?;

    // Create README.md for hooks
    let readme_path = githooks_dir.join("README.md");
    write_file(&readme_path, templates::GITHOOKS_README, force)?;

    // Configure git to use .githooks
    configure_git_hooks(project_path)?;

    println!();
    Ok(())
}

/// Configure git to use .githooks directory
fn configure_git_hooks(project_path: &Path) -> Result<()> {
    use std::process::Command;

    let output = Command::new("git")
        .current_dir(project_path)
        .args(["config", "core.hooksPath", ".githooks"])
        .output();

    match output {
        Ok(output) if output.status.success() => {
            println!("⚙️  Configured git to use .githooks directory");
            Ok(())
        }
        Ok(output) => {
            let stderr = String::from_utf8_lossy(&output.stderr);
            println!("⚠️  Warning: Could not configure git hooks: {}", stderr);
            Ok(())
        }
        Err(e) => {
            println!("⚠️  Warning: Git not found or not initialized: {}", e);
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::tempdir;

    #[test]
    fn test_hooks_pack_creates_files() {
        let temp_dir = tempdir().unwrap();
        let project_path = temp_dir.path();

        apply(project_path, false).unwrap();

        assert!(project_path.join(".githooks").exists());
        assert!(project_path.join(".githooks/pre-push").exists());
        assert!(project_path.join(".githooks/setup.sh").exists());
        assert!(project_path.join(".githooks/README.md").exists());
    }

    #[test]
    #[cfg(unix)]
    fn test_hooks_are_executable() {
        use std::os::unix::fs::PermissionsExt;

        let temp_dir = tempdir().unwrap();
        let project_path = temp_dir.path();

        apply(project_path, false).unwrap();

        let pre_push_perms = std::fs::metadata(project_path.join(".githooks/pre-push"))
            .unwrap()
            .permissions();
        let setup_sh_perms = std::fs::metadata(project_path.join(".githooks/setup.sh"))
            .unwrap()
            .permissions();

        assert_eq!(pre_push_perms.mode() & 0o111, 0o111);
        assert_eq!(setup_sh_perms.mode() & 0o111, 0o111);
    }
}