ggen_core/codegen/
execution_lifecycle.rs1use crate::codegen::{SyncOptions, SyncResult};
2use ggen_utils::error::Result;
3use std::time::Instant;
4
5pub struct ExecutionLifecycle {
6 pre_sync_hooks: Vec<String>,
7 post_sync_hooks: Vec<String>,
8 error_handlers: Vec<String>,
9}
10
11impl ExecutionLifecycle {
12 pub fn new() -> Self {
13 Self {
14 pre_sync_hooks: vec![],
15 post_sync_hooks: vec![],
16 error_handlers: vec![],
17 }
18 }
19
20 pub fn register_pre_sync(&mut self, hook: String) {
21 self.pre_sync_hooks.push(hook);
22 }
23
24 pub fn register_post_sync(&mut self, hook: String) {
25 self.post_sync_hooks.push(hook);
26 }
27
28 pub fn register_error_handler(&mut self, handler: String) {
29 self.error_handlers.push(handler);
30 }
31
32 pub async fn run_pre_sync_hooks(&self, options: &SyncOptions) -> Result<PreSyncContext> {
33 let start = Instant::now();
34 let mut context = PreSyncContext {
35 manifest_path: options.manifest_path.clone(),
36 output_dir: options.output_dir.clone(),
37 dry_run: options.dry_run,
38 verbose: options.verbose,
39 hooks_executed: 0,
40 duration_ms: 0,
41 checks_passed: true,
42 };
43
44 for hook in &self.pre_sync_hooks {
45 if options.verbose {
46 eprintln!("Executing pre-sync hook: {}", hook);
47 }
48 context.hooks_executed += 1;
49 }
50
51 context.duration_ms = start.elapsed().as_millis() as u64;
52 Ok(context)
53 }
54
55 pub async fn run_post_sync_hooks(
56 &self, result: &SyncResult, options: &SyncOptions,
57 ) -> Result<PostSyncContext> {
58 let start = Instant::now();
59 let mut context = PostSyncContext {
60 status: result.status.clone(),
61 files_synced: result.files_synced,
62 duration_ms: result.duration_ms,
63 hooks_executed: 0,
64 artifact_count: result.files.len(),
65 };
66
67 for hook in &self.post_sync_hooks {
68 if options.verbose {
69 eprintln!("Executing post-sync hook: {}", hook);
70 }
71 context.hooks_executed += 1;
72 }
73
74 context.duration_ms = start.elapsed().as_millis() as u64;
75 Ok(context)
76 }
77
78 pub async fn handle_execution_error(
79 &self, _error: &ggen_utils::error::Error, options: &SyncOptions,
80 ) -> Result<()> {
81 for handler in &self.error_handlers {
82 if options.verbose {
83 eprintln!("Executing error handler: {}", handler);
84 }
85 }
86 Ok(())
87 }
88}
89
90pub struct PreSyncContext {
91 pub manifest_path: std::path::PathBuf,
92 pub output_dir: Option<std::path::PathBuf>,
93 pub dry_run: bool,
94 pub verbose: bool,
95 pub hooks_executed: usize,
96 pub duration_ms: u64,
97 pub checks_passed: bool,
98}
99
100pub struct PostSyncContext {
101 pub status: String,
102 pub files_synced: usize,
103 pub duration_ms: u64,
104 pub hooks_executed: usize,
105 pub artifact_count: usize,
106}
107
108impl Default for ExecutionLifecycle {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_lifecycle_creation() {
120 let lifecycle = ExecutionLifecycle::new();
121 assert_eq!(lifecycle.pre_sync_hooks.len(), 0);
122 assert_eq!(lifecycle.post_sync_hooks.len(), 0);
123 }
124
125 #[test]
126 fn test_hook_registration() {
127 let mut lifecycle = ExecutionLifecycle::new();
128 lifecycle.register_pre_sync("hook1".to_string());
129 lifecycle.register_post_sync("hook2".to_string());
130
131 assert_eq!(lifecycle.pre_sync_hooks.len(), 1);
132 assert_eq!(lifecycle.post_sync_hooks.len(), 1);
133 }
134
135 #[tokio::test]
136 async fn test_pre_sync_context() {
137 let lifecycle = ExecutionLifecycle::new();
138 let options = SyncOptions::default();
139
140 let context = lifecycle.run_pre_sync_hooks(&options).await.unwrap();
141 assert!(context.checks_passed);
142 assert_eq!(context.hooks_executed, 0);
143 }
144
145 #[tokio::test]
146 async fn test_post_sync_context() {
147 let lifecycle = ExecutionLifecycle::new();
148 let result = SyncResult {
149 status: "success".to_string(),
150 files_synced: 5,
151 duration_ms: 100,
152 files: vec![],
153 inference_rules_executed: 2,
154 generation_rules_executed: 3,
155 audit_trail: None,
156 error: None,
157 };
158 let options = SyncOptions::default();
159
160 let context = lifecycle
161 .run_post_sync_hooks(&result, &options)
162 .await
163 .unwrap();
164 assert_eq!(context.status, "success");
165 assert_eq!(context.files_synced, 5);
166 }
167
168 #[tokio::test]
169 async fn test_error_handling() {
170 let mut lifecycle = ExecutionLifecycle::new();
171 lifecycle.register_error_handler("error_handler".to_string());
172
173 let error = ggen_utils::error::Error::new("test error");
174 let options = SyncOptions::default();
175
176 let result = lifecycle.handle_execution_error(&error, &options).await;
177 assert!(result.is_ok());
178 }
179}