Skip to main content

rustbridge_core/
lifecycle.rs

1//! Plugin lifecycle state machine (OSGI-inspired)
2
3use serde::{Deserialize, Serialize};
4
5/// Plugin lifecycle states following OSGI-inspired model
6///
7/// State transitions:
8/// ```text
9/// Installed → Starting → Active → Stopping → Stopped
10///                ↑                    │
11///                └────────────────────┘ (restart)
12///            Any state → Failed (on error)
13/// ```
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum LifecycleState {
17    /// Plugin is installed but not yet initialized
18    #[default]
19    Installed,
20    /// Plugin is starting up (initializing runtime, resources)
21    Starting,
22    /// Plugin is fully operational and ready to handle requests
23    Active,
24    /// Plugin is shutting down (cleaning up resources)
25    Stopping,
26    /// Plugin has been stopped
27    Stopped,
28    /// Plugin encountered a fatal error
29    Failed,
30}
31
32impl LifecycleState {
33    /// Check if this state can transition to the target state
34    pub fn can_transition_to(&self, target: LifecycleState) -> bool {
35        use LifecycleState::*;
36        matches!(
37            (self, target),
38            // Normal lifecycle transitions
39            (Installed, Starting)
40                | (Starting, Active)
41                | (Active, Stopping)
42                | (Stopping, Stopped)
43                // Restart from stopped
44                | (Stopped, Starting)
45                // Any state can fail
46                | (Installed, Failed)
47                | (Starting, Failed)
48                | (Active, Failed)
49                | (Stopping, Failed)
50        )
51    }
52
53    /// Check if the plugin can handle requests in this state
54    pub fn can_handle_requests(&self) -> bool {
55        matches!(self, LifecycleState::Active)
56    }
57
58    /// Check if the plugin is in a terminal state
59    pub fn is_terminal(&self) -> bool {
60        matches!(self, LifecycleState::Stopped | LifecycleState::Failed)
61    }
62
63    /// Get a human-readable description of this state
64    pub fn description(&self) -> &'static str {
65        match self {
66            LifecycleState::Installed => "Plugin is installed but not initialized",
67            LifecycleState::Starting => "Plugin is starting up",
68            LifecycleState::Active => "Plugin is active and ready",
69            LifecycleState::Stopping => "Plugin is shutting down",
70            LifecycleState::Stopped => "Plugin has stopped",
71            LifecycleState::Failed => "Plugin has failed",
72        }
73    }
74}
75
76impl std::fmt::Display for LifecycleState {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            LifecycleState::Installed => write!(f, "Installed"),
80            LifecycleState::Starting => write!(f, "Starting"),
81            LifecycleState::Active => write!(f, "Active"),
82            LifecycleState::Stopping => write!(f, "Stopping"),
83            LifecycleState::Stopped => write!(f, "Stopped"),
84            LifecycleState::Failed => write!(f, "Failed"),
85        }
86    }
87}
88
89#[cfg(test)]
90#[path = "lifecycle/lifecycle_tests.rs"]
91mod lifecycle_tests;
92
93#[cfg(test)]
94#[path = "lifecycle/lifecycle_parameterized_tests.rs"]
95mod lifecycle_parameterized_tests;