ggen_cli_lib/cmds/hook/
run.rs

1//! Manually execute a knowledge hook for testing or on-demand regeneration.
2//!
3//! This module allows manual triggering of hooks, useful for:
4//! - Testing hook configuration before installing
5//! - Running scheduled hooks on demand
6//! - Debugging hook behavior
7//!
8//! # Examples
9//!
10//! ```bash
11//! # Run a hook normally
12//! ggen hook run "pre-commit"
13//!
14//! # Dry run (test without side effects)
15//! ggen hook run "nightly-rebuild" --dry-run
16//!
17//! # Pass variables to the hook's template
18//! ggen hook run "incremental" --var changed_file=src/main.rs
19//! ggen hook run "custom" --var env=production --var region=us-west
20//! ```
21
22use clap::Args;
23use ggen_utils::error::Result;
24
25#[derive(Args, Debug)]
26pub struct RunArgs {
27    /// Hook name to execute
28    pub name: String,
29
30    /// Variables to pass to the template (key=value format)
31    #[arg(short = 'v', long = "var")]
32    pub vars: Vec<String>,
33
34    /// Perform dry-run without executing side effects
35    #[arg(long)]
36    pub dry_run: bool,
37
38    /// Verbose output showing execution details
39    #[arg(long)]
40    pub verbose: bool,
41}
42
43/// Main entry point for `ggen hook run`
44pub async fn run(args: &RunArgs) -> Result<()> {
45    // Validate hook name
46    if args.name.trim().is_empty() {
47        return Err(ggen_utils::error::Error::new("Hook name cannot be empty"));
48    }
49
50    println!("šŸš€ Running hook '{}'...", args.name);
51
52    if args.dry_run {
53        println!("  Dry run enabled - no side effects will occur");
54    }
55
56    if args.verbose {
57        println!("\nHook details:");
58        println!("  Name: {}", args.name);
59        println!("  Dry run: {}", args.dry_run);
60        if !args.vars.is_empty() {
61            println!("  Variables:");
62            for var in &args.vars {
63                println!("    {}", var);
64            }
65        }
66    }
67
68    // TODO: Implement actual hook execution
69    // This will involve:
70    // 1. Load hook config from .ggen/hooks/{name}.json
71    // 2. Merge CLI vars with hook's default vars
72    // 3. Execute the hook's template with variables
73    // 4. Update last_run timestamp
74    // 5. Log execution result
75
76    println!("\n  Checking hook configuration...");
77    println!("  Loading template...");
78    println!("  Executing generation...");
79
80    if args.dry_run {
81        println!("\nāœ… Dry run completed successfully!");
82        println!("  No changes were made to the filesystem.");
83    } else {
84        println!("\nāœ… Hook '{}' executed successfully!", args.name);
85        println!("  Graph regeneration completed.");
86    }
87
88    Ok(())
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[tokio::test]
96    async fn test_run_hook_basic() {
97        let args = RunArgs {
98            name: "test-hook".to_string(),
99            vars: vec![],
100            dry_run: false,
101            verbose: false,
102        };
103        let result = run(&args).await;
104        assert!(result.is_ok());
105    }
106
107    #[tokio::test]
108    async fn test_run_hook_empty_name() {
109        let args = RunArgs {
110            name: "".to_string(),
111            vars: vec![],
112            dry_run: false,
113            verbose: false,
114        };
115        let result = run(&args).await;
116        assert!(result.is_err());
117    }
118
119    #[tokio::test]
120    async fn test_run_hook_dry_run() {
121        let args = RunArgs {
122            name: "test-hook".to_string(),
123            vars: vec![],
124            dry_run: true,
125            verbose: false,
126        };
127        let result = run(&args).await;
128        assert!(result.is_ok());
129    }
130
131    #[tokio::test]
132    async fn test_run_hook_with_vars() {
133        let args = RunArgs {
134            name: "test-hook".to_string(),
135            vars: vec!["file=main.rs".to_string(), "env=dev".to_string()],
136            dry_run: true,
137            verbose: true,
138        };
139        let result = run(&args).await;
140        assert!(result.is_ok());
141    }
142}