Skip to main content

Grove/Host/
ExtensionHost.rs

1//! Extension Host Module
2//!
3//! Main extension host controller for Grove.
4//! Manages the overall host lifecycle and coordinates extension execution.
5
6use std::{path::PathBuf, sync::Arc};
7
8use anyhow::{Context, Result};
9use serde::{Deserialize, Serialize};
10use tokio::sync::RwLock;
11
12use crate::{
13	Host::{Activation, ExtensionManager::ExtensionManagerImpl, HostConfig},
14	Transport::Strategy::Transport,
15	WASM::Runtime::{WASMConfig, WASMRuntime},
16	dev_log,
17};
18
19/// Main extension host controller
20pub struct ExtensionHostImpl {
21	/// Host configuration
22	#[allow(dead_code)]
23	config:HostConfig,
24	/// Transport for communication
25	transport:Transport,
26	/// Extension manager
27	extension_manager:Arc<ExtensionManagerImpl>,
28	/// Activation engine
29	activation_engine:Arc<Activation::ActivationEngine>,
30	/// WASM runtime
31	wasm_runtime:Arc<WASMRuntime>,
32	/// Active extensions
33	active_extensions:Arc<RwLock<Vec<String>>>,
34	/// Host state
35	state:Arc<RwLock<HostState>>,
36}
37
38/// Host state
39#[derive(Debug, Clone, PartialEq)]
40pub enum HostState {
41	/// Host has been created but not initialized
42	Created,
43	/// Host is ready to accept extensions
44	Ready,
45	/// Host is running with active extensions
46	Running,
47	/// Host is shutting down
48	ShuttingDown,
49	/// Host has been terminated
50	Terminated,
51}
52
53/// Host statistics
54#[derive(Debug, Clone, Default, Serialize, Deserialize)]
55pub struct HostStats {
56	/// Number of loaded extensions
57	pub loaded_extensions:usize,
58	/// Number of active extensions
59	pub active_extensions:usize,
60	/// Total number of activations
61	pub total_activations:u64,
62	/// Total activation time in milliseconds
63	pub total_activation_time_ms:u64,
64	/// Number of API calls made
65	pub api_calls:u64,
66	/// Number of errors encountered
67	pub errors:u64,
68	/// Host uptime in seconds
69	pub uptime_seconds:u64,
70}
71
72impl ExtensionHostImpl {
73	/// Create a new extension host
74	///
75	/// # Arguments
76	///
77	/// * `transport` - The communication transport to use
78	///
79	/// # Example
80	///
81	/// ```rust,no_run
82	/// use grove::{ExtensionHost, Transport};
83	///
84	/// # async fn example() -> anyhow::Result<()> {
85	/// let transport = Transport::default();
86	/// let host = ExtensionHost::new(transport).await?;
87	/// # Ok(())
88	/// # }
89	/// ```
90	pub async fn new(transport:Transport) -> Result<Self> { Self::with_config(transport, HostConfig::default()).await }
91
92	/// Create a new extension host with custom configuration
93	pub async fn with_config(transport:Transport, config:HostConfig) -> Result<Self> {
94		dev_log!("grove", "Creating extension host with config: {:?}", config);
95
96		// Connect transport
97		transport.connect().await.context("Failed to connect transport")?;
98
99		// Create WASM runtime
100		let wasm_config = WASMConfig::new(512, 30000, true);
101		let wasm_runtime = Arc::new(WASMRuntime::new(wasm_config).await?);
102
103		// Create extension manager
104		let extension_manager = Arc::new(ExtensionManagerImpl::new(Arc::clone(&wasm_runtime), config.clone()));
105
106		// Create activation engine
107		let activation_engine = Arc::new(Activation::ActivationEngine::new(
108			Arc::clone(&extension_manager),
109			config.clone(),
110		));
111
112		dev_log!("grove", "Extension host created successfully");
113
114		Ok(Self {
115			config,
116			transport,
117			extension_manager,
118			activation_engine,
119			wasm_runtime,
120			active_extensions:Arc::new(RwLock::new(Vec::new())),
121			state:Arc::new(RwLock::new(HostState::Created)),
122		})
123	}
124
125	/// Load an extension from a path
126	pub async fn load_extension(&self, path:&PathBuf) -> Result<String> {
127		dev_log!("extensions", "Loading extension from: {:?}", path);
128
129		let extension_id = self
130			.extension_manager
131			.load_extension(path)
132			.await
133			.context("Failed to load extension")?;
134
135		dev_log!("extensions", "Extension loaded: {}", extension_id);
136
137		*self.state.write().await = HostState::Ready;
138
139		Ok(extension_id)
140	}
141
142	/// Unload an extension
143	pub async fn unload_extension(&self, extension_id:&str) -> Result<()> {
144		dev_log!("extensions", "Unloading extension: {}", extension_id);
145
146		self.extension_manager
147			.unload_extension(extension_id)
148			.await
149			.context("Failed to unload extension")?;
150
151		dev_log!("extensions", "Extension unloaded: {}", extension_id);
152
153		Ok(())
154	}
155
156	/// Activate an extension
157	pub async fn activate(&self, extension_id:&str) -> Result<()> {
158		dev_log!("extensions", "Activating extension: {}", extension_id);
159
160		let start = std::time::Instant::now();
161
162		let result = self
163			.activation_engine
164			.activate(extension_id)
165			.await
166			.context("Failed to activate extension")?;
167
168		let elapsed = start.elapsed().as_millis() as u64;
169
170		if result.success {
171			dev_log!("extensions", "Extension activated in {}ms: {}", elapsed, extension_id);
172
173			// Track active extension
174			let mut active = self.active_extensions.write().await;
175			if !active.contains(&extension_id.to_string()) {
176				active.push(extension_id.to_string());
177			}
178
179			*self.state.write().await = HostState::Running;
180		} else {
181			dev_log!("extensions", "error: extension activation failed: {}", extension_id);
182		}
183
184		Ok(())
185	}
186
187	/// Deactivate an extension
188	pub async fn deactivate(&self, extension_id:&str) -> Result<()> {
189		dev_log!("extensions", "Deactivating extension: {}", extension_id);
190
191		self.activation_engine
192			.deactivate(extension_id)
193			.await
194			.context("Failed to deactivate extension")?;
195
196		// Remove from active extensions
197		let mut active = self.active_extensions.write().await;
198		active.retain(|id| id != extension_id);
199
200		dev_log!("extensions", "Extension deactivated: {}", extension_id);
201
202		Ok(())
203	}
204
205	/// Activate all loaded extensions
206	pub async fn activate_all(&self) -> Result<Vec<String>> {
207		dev_log!("extensions", "Activating all extensions");
208
209		let extensions = self.extension_manager.list_extensions().await;
210		let mut activated = Vec::new();
211		let mut failed = Vec::new();
212
213		for extension_id in extensions {
214			match self.activate(&extension_id).await {
215				Ok(_) => activated.push(extension_id),
216				Err(e) => {
217					dev_log!("extensions", "error: failed to activate {}: {}", extension_id, e);
218					failed.push(extension_id);
219				},
220			}
221		}
222
223		dev_log!(
224			"extensions",
225			"warn: activated {} extensions, {} failed",
226			activated.len(),
227			failed.len()
228		);
229
230		Ok(activated)
231	}
232
233	/// Deactivate all active extensions
234	pub async fn deactivate_all(&self) -> Result<()> {
235		dev_log!("extensions", "Deactivating all extensions");
236
237		let active = self.active_extensions.read().await.clone();
238
239		for extension_id in active {
240			if let Err(e) = self.deactivate(&extension_id).await {
241				dev_log!("extensions", "error: failed to deactivate {}: {}", extension_id, e);
242			}
243		}
244
245		*self.state.write().await = HostState::Ready;
246
247		Ok(())
248	}
249
250	/// Get host statistics
251	pub async fn stats(&self) -> HostStats {
252		let active_extensions = self.active_extensions.read().await.len();
253		let loaded_extensions = self.extension_manager.list_extensions().await.len();
254		let extension_stats = self.extension_manager.stats().await;
255
256		HostStats {
257			loaded_extensions,
258			active_extensions,
259			total_activations:extension_stats.total_activated as u64,
260			total_activation_time_ms:extension_stats.total_activation_time_ms,
261			api_calls:0, // Track through API bridge
262			errors:extension_stats.errors,
263			uptime_seconds:0, // Track from host start time
264		}
265	}
266
267	/// Get host state
268	pub async fn state(&self) -> HostState { self.state.read().await.clone() }
269
270	/// Get the transport
271	pub fn transport(&self) -> &Transport { &self.transport }
272
273	/// Get the extension manager
274	pub fn extension_manager(&self) -> &Arc<ExtensionManagerImpl> { &self.extension_manager }
275
276	/// Get the activation engine
277	pub fn activation_engine(&self) -> &Arc<Activation::ActivationEngine> { &self.activation_engine }
278
279	/// Get the WASM runtime
280	pub fn wasm_runtime(&self) -> &Arc<WASMRuntime> { &self.wasm_runtime }
281
282	/// Shutdown the host and clean up resources
283	pub async fn shutdown(&self) -> Result<()> {
284		dev_log!("lifecycle", "Shutting down extension host");
285
286		*self.state.write().await = HostState::ShuttingDown;
287
288		// Deactivate all extensions
289		if let Err(e) = self.deactivate_all().await {
290			dev_log!("lifecycle", "error: error deactivating extensions during shutdown: {}", e);
291		}
292
293		// Close transport
294		if let Err(e) = self.transport.close().await {
295			dev_log!("lifecycle", "error: error closing transport during shutdown: {}", e);
296		}
297
298		// Shutdown WASM runtime
299		if let Err(e) = self.wasm_runtime.shutdown().await {
300			dev_log!("wasm", "error: error shutting down WASM runtime: {}", e);
301		}
302
303		*self.state.write().await = HostState::Terminated;
304
305		dev_log!("lifecycle", "Extension host shutdown complete");
306
307		Ok(())
308	}
309}
310
311impl Drop for ExtensionHostImpl {
312	fn drop(&mut self) {
313		dev_log!("lifecycle", "ExtensionHost dropped");
314	}
315}
316
317#[cfg(test)]
318mod tests {
319	use super::*;
320
321	#[tokio::test]
322	async fn test_host_state() {
323		assert_eq!(HostState::Created, HostState::Created);
324		assert_eq!(HostState::Ready, HostState::Ready);
325		assert_eq!(HostState::Running, HostState::Running);
326	}
327
328	#[test]
329	fn test_host_stats_default() {
330		let stats = HostStats::default();
331		assert_eq!(stats.loaded_extensions, 0);
332		assert_eq!(stats.active_extensions, 0);
333	}
334
335	#[test]
336	fn test_host_config_default() {
337		let config = HostConfig::default();
338		assert_eq!(config.max_extensions, 100);
339		assert_eq!(config.lazy_activation, true);
340	}
341}