blvm_sdk/composition/
lifecycle.rs1use crate::composition::registry::ModuleRegistry;
6use crate::composition::types::*;
7use blvm_node::module::manager::ModuleManager;
8use blvm_node::module::traits::{
9 ModuleMetadata as RefModuleMetadata, ModuleState as RefModuleState,
10};
11use std::collections::HashMap;
12use std::sync::Arc;
13use tokio::sync::Mutex;
14
15fn module_state_to_status(s: RefModuleState) -> ModuleStatus {
16 match s {
17 RefModuleState::Running => ModuleStatus::Running,
18 RefModuleState::Stopped => ModuleStatus::Stopped,
19 RefModuleState::Initializing => ModuleStatus::Initializing,
20 RefModuleState::Stopping => ModuleStatus::Stopping,
21 RefModuleState::Error(msg) => ModuleStatus::Error(msg),
22 }
23}
24
25pub struct ModuleLifecycle {
27 pub(crate) registry: ModuleRegistry,
29 module_manager: Option<Arc<Mutex<ModuleManager>>>,
31 status_cache: HashMap<String, ModuleStatus>,
33}
34
35impl ModuleLifecycle {
36 pub fn new(registry: ModuleRegistry) -> Self {
38 Self {
39 registry,
40 module_manager: None,
41 status_cache: HashMap::new(),
42 }
43 }
44
45 pub fn with_module_manager(mut self, manager: Arc<Mutex<ModuleManager>>) -> Self {
47 self.module_manager = Some(manager);
48 self
49 }
50
51 pub async fn start_module(
53 &mut self,
54 name: &str,
55 config: Option<&HashMap<String, serde_json::Value>>,
56 ) -> Result<()> {
57 if self.module_manager.is_none() {
58 return Err(CompositionError::InstallationFailed(
59 "ModuleManager required to start modules; use ModuleLifecycle::with_module_manager"
60 .to_string(),
61 ));
62 }
63
64 let info = self.registry.get_module(name, None)?;
65
66 let config_map: HashMap<String, String> = config
67 .map(|c| {
68 c.iter()
69 .map(|(k, v)| {
70 let s = match v {
71 serde_json::Value::String(s) => s.clone(),
72 _ => v.to_string(),
73 };
74 (k.clone(), s)
75 })
76 .collect()
77 })
78 .unwrap_or_default();
79
80 if let Some(ref manager) = self.module_manager {
81 let metadata: RefModuleMetadata = info.clone().into();
82
83 let binary_path = info.binary_path.as_ref().ok_or_else(|| {
84 CompositionError::ModuleNotFound(format!("Module {name} has no binary path"))
85 })?;
86
87 let mut mgr = manager.lock().await;
88 mgr.load_module(&info.name, binary_path, metadata, config_map)
89 .await
90 .map_err(CompositionError::from)?;
91
92 self.status_cache
93 .insert(name.to_string(), ModuleStatus::Running);
94 }
95
96 Ok(())
97 }
98
99 pub async fn stop_module(&mut self, name: &str) -> Result<()> {
101 let _info = self.registry.get_module(name, None)?;
102
103 if let Some(ref manager) = self.module_manager {
104 let mut mgr = manager.lock().await;
105 mgr.unload_module(name)
106 .await
107 .map_err(CompositionError::from)?;
108 }
109
110 self.status_cache
111 .insert(name.to_string(), ModuleStatus::Stopped);
112 Ok(())
113 }
114
115 pub async fn restart_module(
117 &mut self,
118 name: &str,
119 config: Option<&HashMap<String, serde_json::Value>>,
120 ) -> Result<()> {
121 self.stop_module(name).await?;
122 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
123 self.start_module(name, config).await
124 }
125
126 pub async fn get_module_status(&self, name: &str) -> Result<ModuleStatus> {
128 let _ = self.registry.get_module(name, None)?;
129
130 if let Some(ref manager) = self.module_manager {
131 if let Some(state) = manager.lock().await.get_module_state(name).await {
132 return Ok(module_state_to_status(state));
133 }
134 }
135
136 Ok(self
137 .status_cache
138 .get(name)
139 .cloned()
140 .unwrap_or(ModuleStatus::NotInstalled))
141 }
142
143 pub async fn health_check(&self, name: &str) -> Result<ModuleHealth> {
145 let status = self.get_module_status(name).await?;
146 match status {
147 ModuleStatus::Running => Ok(ModuleHealth::Healthy),
148 ModuleStatus::Error(msg) => Ok(ModuleHealth::Unhealthy(msg)),
149 ModuleStatus::Stopped | ModuleStatus::NotInstalled => Ok(ModuleHealth::Unknown),
150 _ => Ok(ModuleHealth::Degraded),
151 }
152 }
153
154 pub fn registry(&self) -> &ModuleRegistry {
156 &self.registry
157 }
158
159 pub fn registry_mut(&mut self) -> &mut ModuleRegistry {
161 &mut self.registry
162 }
163}