1use std::time::Instant;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum LifecycleState {
8 Created,
10 Initialized,
12 Running,
14 Stopped,
16 Unloaded,
18 Error,
20}
21
22impl LifecycleState {
23 pub fn can_start(&self) -> bool {
25 matches!(self, Self::Initialized)
26 }
27
28 pub fn can_stop(&self) -> bool {
30 matches!(self, Self::Running)
31 }
32
33 pub fn can_call(&self) -> bool {
35 matches!(self, Self::Running)
36 }
37
38 pub fn can_reload(&self) -> bool {
40 matches!(self, Self::Initialized | Self::Running | Self::Stopped | Self::Error)
41 }
42
43 pub fn is_terminal(&self) -> bool {
45 matches!(self, Self::Unloaded)
46 }
47
48 pub fn description(&self) -> &'static str {
50 match self {
51 Self::Created => "Plugin created but not initialized",
52 Self::Initialized => "Plugin initialized and ready to start",
53 Self::Running => "Plugin running and accepting calls",
54 Self::Stopped => "Plugin stopped",
55 Self::Unloaded => "Plugin unloaded",
56 Self::Error => "Plugin in error state",
57 }
58 }
59}
60
61impl std::fmt::Display for LifecycleState {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let name = match self {
64 Self::Created => "created",
65 Self::Initialized => "initialized",
66 Self::Running => "running",
67 Self::Stopped => "stopped",
68 Self::Unloaded => "unloaded",
69 Self::Error => "error",
70 };
71 write!(f, "{}", name)
72 }
73}
74
75pub trait PluginLifecycle {
77 fn on_init(&mut self) -> crate::Result<()> {
79 Ok(())
80 }
81
82 fn on_start(&mut self) -> crate::Result<()> {
84 Ok(())
85 }
86
87 fn on_stop(&mut self) -> crate::Result<()> {
89 Ok(())
90 }
91
92 fn on_unload(&mut self) -> crate::Result<()> {
94 Ok(())
95 }
96
97 fn on_before_reload(&mut self) -> crate::Result<()> {
99 Ok(())
100 }
101
102 fn on_after_reload(&mut self) -> crate::Result<()> {
104 Ok(())
105 }
106
107 fn on_error(&mut self, error: &crate::Error) {
109 tracing::error!("Plugin error: {}", error);
110 }
111}
112
113#[derive(Debug, Clone)]
115pub enum LifecycleEvent {
116 Created {
118 name: String,
120 at: Instant,
122 },
123 Initialized {
125 name: String,
127 at: Instant,
129 },
130 Started {
132 name: String,
134 at: Instant,
136 },
137 Stopped {
139 name: String,
141 at: Instant,
143 },
144 Reloaded {
146 name: String,
148 at: Instant,
150 count: u64,
152 },
153 Unloaded {
155 name: String,
157 at: Instant,
159 },
160 Error {
162 name: String,
164 message: String,
166 at: Instant,
168 },
169}
170
171impl LifecycleEvent {
172 pub fn plugin_name(&self) -> &str {
174 match self {
175 Self::Created { name, .. } => name,
176 Self::Initialized { name, .. } => name,
177 Self::Started { name, .. } => name,
178 Self::Stopped { name, .. } => name,
179 Self::Reloaded { name, .. } => name,
180 Self::Unloaded { name, .. } => name,
181 Self::Error { name, .. } => name,
182 }
183 }
184
185 pub fn timestamp(&self) -> Instant {
187 match self {
188 Self::Created { at, .. } => *at,
189 Self::Initialized { at, .. } => *at,
190 Self::Started { at, .. } => *at,
191 Self::Stopped { at, .. } => *at,
192 Self::Reloaded { at, .. } => *at,
193 Self::Unloaded { at, .. } => *at,
194 Self::Error { at, .. } => *at,
195 }
196 }
197
198 pub fn event_name(&self) -> &'static str {
200 match self {
201 Self::Created { .. } => "created",
202 Self::Initialized { .. } => "initialized",
203 Self::Started { .. } => "started",
204 Self::Stopped { .. } => "stopped",
205 Self::Reloaded { .. } => "reloaded",
206 Self::Unloaded { .. } => "unloaded",
207 Self::Error { .. } => "error",
208 }
209 }
210}
211
212pub struct LifecycleHooks {
214 handlers: Vec<Box<dyn Fn(&LifecycleEvent) + Send + Sync>>,
215}
216
217impl LifecycleHooks {
218 pub fn new() -> Self {
220 Self {
221 handlers: Vec::new(),
222 }
223 }
224
225 pub fn on_event<F>(&mut self, handler: F)
227 where
228 F: Fn(&LifecycleEvent) + Send + Sync + 'static,
229 {
230 self.handlers.push(Box::new(handler));
231 }
232
233 pub fn emit(&self, event: LifecycleEvent) {
235 for handler in &self.handlers {
236 handler(&event);
237 }
238 }
239
240 pub fn emit_created(&self, name: &str) {
242 self.emit(LifecycleEvent::Created {
243 name: name.to_string(),
244 at: Instant::now(),
245 });
246 }
247
248 pub fn emit_initialized(&self, name: &str) {
250 self.emit(LifecycleEvent::Initialized {
251 name: name.to_string(),
252 at: Instant::now(),
253 });
254 }
255
256 pub fn emit_started(&self, name: &str) {
258 self.emit(LifecycleEvent::Started {
259 name: name.to_string(),
260 at: Instant::now(),
261 });
262 }
263
264 pub fn emit_stopped(&self, name: &str) {
266 self.emit(LifecycleEvent::Stopped {
267 name: name.to_string(),
268 at: Instant::now(),
269 });
270 }
271
272 pub fn emit_reloaded(&self, name: &str, count: u64) {
274 self.emit(LifecycleEvent::Reloaded {
275 name: name.to_string(),
276 at: Instant::now(),
277 count,
278 });
279 }
280
281 pub fn emit_unloaded(&self, name: &str) {
283 self.emit(LifecycleEvent::Unloaded {
284 name: name.to_string(),
285 at: Instant::now(),
286 });
287 }
288
289 pub fn emit_error(&self, name: &str, message: &str) {
291 self.emit(LifecycleEvent::Error {
292 name: name.to_string(),
293 message: message.to_string(),
294 at: Instant::now(),
295 });
296 }
297}
298
299impl Default for LifecycleHooks {
300 fn default() -> Self {
301 Self::new()
302 }
303}
304
305impl std::fmt::Debug for LifecycleHooks {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 f.debug_struct("LifecycleHooks")
308 .field("handler_count", &self.handlers.len())
309 .finish()
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use std::sync::atomic::{AtomicUsize, Ordering};
317 use std::sync::Arc;
318
319 #[test]
320 fn test_lifecycle_state_transitions() {
321 assert!(LifecycleState::Initialized.can_start());
322 assert!(!LifecycleState::Created.can_start());
323
324 assert!(LifecycleState::Running.can_stop());
325 assert!(!LifecycleState::Stopped.can_stop());
326
327 assert!(LifecycleState::Running.can_call());
328 assert!(!LifecycleState::Stopped.can_call());
329
330 assert!(LifecycleState::Running.can_reload());
331 assert!(!LifecycleState::Unloaded.can_reload());
332
333 assert!(LifecycleState::Unloaded.is_terminal());
334 assert!(!LifecycleState::Running.is_terminal());
335 }
336
337 #[test]
338 fn test_lifecycle_hooks() {
339 let counter = Arc::new(AtomicUsize::new(0));
340 let counter_clone = counter.clone();
341
342 let mut hooks = LifecycleHooks::new();
343 hooks.on_event(move |_| {
344 counter_clone.fetch_add(1, Ordering::Relaxed);
345 });
346
347 hooks.emit_created("test");
348 hooks.emit_started("test");
349 hooks.emit_stopped("test");
350
351 assert_eq!(counter.load(Ordering::Relaxed), 3);
352 }
353
354 #[test]
355 fn test_lifecycle_event_info() {
356 let event = LifecycleEvent::Started {
357 name: "test-plugin".to_string(),
358 at: Instant::now(),
359 };
360
361 assert_eq!(event.plugin_name(), "test-plugin");
362 assert_eq!(event.event_name(), "started");
363 }
364}