ggen-core 26.7.3

Core graph-aware code generation engine
Documentation
use crate::codegen::{SyncOptions, SyncResult};
use crate::utils::error::Result;
use std::time::Instant;

pub struct ExecutionLifecycle {
    pre_sync_hooks: Vec<String>,
    post_sync_hooks: Vec<String>,
    error_handlers: Vec<String>,
}

impl ExecutionLifecycle {
    pub fn new() -> Self {
        Self {
            pre_sync_hooks: vec![],
            post_sync_hooks: vec![],
            error_handlers: vec![],
        }
    }

    pub fn register_pre_sync(&mut self, hook: String) {
        self.pre_sync_hooks.push(hook);
    }

    pub fn register_post_sync(&mut self, hook: String) {
        self.post_sync_hooks.push(hook);
    }

    pub fn register_error_handler(&mut self, handler: String) {
        self.error_handlers.push(handler);
    }

    pub async fn run_pre_sync_hooks(&self, options: &SyncOptions) -> Result<PreSyncContext> {
        let start = Instant::now();
        let mut context = PreSyncContext {
            manifest_path: options.manifest_path.clone(),
            output_dir: options.output_dir.clone(),
            dry_run: options.flags.mode.dry_run,
            verbose: options.flags.behavior.verbose,
            hooks_executed: 0,
            duration_ms: 0,
            checks_passed: true,
        };

        for hook in &self.pre_sync_hooks {
            if options.flags.behavior.verbose {
                eprintln!("Executing pre-sync hook: {}", hook);
            }
            context.hooks_executed += 1;
        }

        context.duration_ms = start.elapsed().as_millis() as u64;
        Ok(context)
    }

    pub async fn run_post_sync_hooks(
        &self, result: &SyncResult, options: &SyncOptions,
    ) -> Result<PostSyncContext> {
        let start = Instant::now();
        let mut context = PostSyncContext {
            status: result.status.clone(),
            files_synced: result.files_synced,
            duration_ms: result.duration_ms,
            hooks_executed: 0,
            artifact_count: result.files.len(),
        };

        for hook in &self.post_sync_hooks {
            if options.flags.behavior.verbose {
                eprintln!("Executing post-sync hook: {}", hook);
            }
            context.hooks_executed += 1;
        }

        context.duration_ms = start.elapsed().as_millis() as u64;
        Ok(context)
    }

    pub async fn handle_execution_error(
        &self, _error: &crate::utils::error::Error, options: &SyncOptions,
    ) -> Result<()> {
        for handler in &self.error_handlers {
            if options.flags.behavior.verbose {
                eprintln!("Executing error handler: {}", handler);
            }
        }
        Ok(())
    }
}

pub struct PreSyncContext {
    pub manifest_path: std::path::PathBuf,
    pub output_dir: Option<std::path::PathBuf>,
    pub dry_run: bool,
    pub verbose: bool,
    pub hooks_executed: usize,
    pub duration_ms: u64,
    pub checks_passed: bool,
}

pub struct PostSyncContext {
    pub status: String,
    pub files_synced: usize,
    pub duration_ms: u64,
    pub hooks_executed: usize,
    pub artifact_count: usize,
}

impl Default for ExecutionLifecycle {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_lifecycle_creation() {
        let lifecycle = ExecutionLifecycle::new();
        assert_eq!(lifecycle.pre_sync_hooks.len(), 0);
        assert_eq!(lifecycle.post_sync_hooks.len(), 0);
    }

    #[test]
    fn test_hook_registration() {
        let mut lifecycle = ExecutionLifecycle::new();
        lifecycle.register_pre_sync("hook1".to_string());
        lifecycle.register_post_sync("hook2".to_string());

        assert_eq!(lifecycle.pre_sync_hooks.len(), 1);
        assert_eq!(lifecycle.post_sync_hooks.len(), 1);
    }

    #[tokio::test]
    async fn test_pre_sync_context() {
        let lifecycle = ExecutionLifecycle::new();
        let options = SyncOptions::default();

        let context = lifecycle.run_pre_sync_hooks(&options).await.unwrap();
        assert!(context.checks_passed);
        assert_eq!(context.hooks_executed, 0);
    }

    #[tokio::test]
    async fn test_post_sync_context() {
        let lifecycle = ExecutionLifecycle::new();
        let result = SyncResult {
            status: "success".to_string(),
            files_synced: 5,
            duration_ms: 100,
            files: vec![],
            inference_rules_executed: 2,
            generation_rules_executed: 3,
            audit_trail: None,
            error: None,
            recovery: None,
            andon_signal: None,
        };
        let options = SyncOptions::default();

        let context = lifecycle
            .run_post_sync_hooks(&result, &options)
            .await
            .unwrap();
        assert_eq!(context.status, "success");
        assert_eq!(context.files_synced, 5);
    }

    #[tokio::test]
    async fn test_error_handling() {
        let mut lifecycle = ExecutionLifecycle::new();
        lifecycle.register_error_handler("error_handler".to_string());

        let error = crate::utils::error::Error::new("test error");
        let options = SyncOptions::default();

        let result = lifecycle.handle_execution_error(&error, &options).await;
        assert!(result.is_ok());
    }
}