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;
11use crate::dev_log;
12
13use crate::{
14	Host::{Activation, ExtensionManager::ExtensionManagerImpl, HostConfig},
15	Transport::Strategy::Transport,
16	WASM::Runtime::{WASMConfig, WASMRuntime},
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!("extensions", "warn: activated {} extensions, {} failed", activated.len(), failed.len());
224
225		Ok(activated)
226	}
227
228	/// Deactivate all active extensions
229		pub async fn deactivate_all(&self) -> Result<()> {
230		dev_log!("extensions", "Deactivating all extensions");
231
232		let active = self.active_extensions.read().await.clone();
233
234		for extension_id in active {
235			if let Err(e) = self.deactivate(&extension_id).await {
236				dev_log!("extensions", "error: failed to deactivate {}: {}", extension_id, e);
237			}
238		}
239
240		*self.state.write().await = HostState::Ready;
241
242		Ok(())
243	}
244
245	/// Get host statistics
246	pub async fn stats(&self) -> HostStats {
247		let active_extensions = self.active_extensions.read().await.len();
248		let loaded_extensions = self.extension_manager.list_extensions().await.len();
249		let extension_stats = self.extension_manager.stats().await;
250
251		HostStats {
252			loaded_extensions,
253			active_extensions,
254			total_activations:extension_stats.total_activated as u64,
255			total_activation_time_ms:extension_stats.total_activation_time_ms,
256			api_calls:0, // Track through API bridge
257			errors:extension_stats.errors,
258			uptime_seconds:0, // Track from host start time
259		}
260	}
261
262	/// Get host state
263	pub async fn state(&self) -> HostState { self.state.read().await.clone() }
264
265	/// Get the transport
266	pub fn transport(&self) -> &Transport { &self.transport }
267
268	/// Get the extension manager
269	pub fn extension_manager(&self) -> &Arc<ExtensionManagerImpl> { &self.extension_manager }
270
271	/// Get the activation engine
272	pub fn activation_engine(&self) -> &Arc<Activation::ActivationEngine> { &self.activation_engine }
273
274	/// Get the WASM runtime
275	pub fn wasm_runtime(&self) -> &Arc<WASMRuntime> { &self.wasm_runtime }
276
277	/// Shutdown the host and clean up resources
278		pub async fn shutdown(&self) -> Result<()> {
279		dev_log!("lifecycle", "Shutting down extension host");
280
281		*self.state.write().await = HostState::ShuttingDown;
282
283		// Deactivate all extensions
284		if let Err(e) = self.deactivate_all().await {
285			dev_log!("lifecycle", "error: error deactivating extensions during shutdown: {}", e);
286		}
287
288		// Close transport
289		if let Err(e) = self.transport.close().await {
290			dev_log!("lifecycle", "error: error closing transport during shutdown: {}", e);
291		}
292
293		// Shutdown WASM runtime
294		if let Err(e) = self.wasm_runtime.shutdown().await {
295			dev_log!("wasm", "error: error shutting down WASM runtime: {}", e);
296		}
297
298		*self.state.write().await = HostState::Terminated;
299
300		dev_log!("lifecycle", "Extension host shutdown complete");
301
302		Ok(())
303	}
304}
305
306impl Drop for ExtensionHostImpl {
307	fn drop(&mut self) {
308		dev_log!("lifecycle", "ExtensionHost dropped");
309	}
310}
311
312#[cfg(test)]
313mod tests {
314	use super::*;
315
316	#[tokio::test]
317	async fn test_host_state() {
318		assert_eq!(HostState::Created, HostState::Created);
319		assert_eq!(HostState::Ready, HostState::Ready);
320		assert_eq!(HostState::Running, HostState::Running);
321	}
322
323	#[test]
324	fn test_host_stats_default() {
325		let stats = HostStats::default();
326		assert_eq!(stats.loaded_extensions, 0);
327		assert_eq!(stats.active_extensions, 0);
328	}
329
330	#[test]
331	fn test_host_config_default() {
332		let config = HostConfig::default();
333		assert_eq!(config.max_extensions, 100);
334		assert_eq!(config.lazy_activation, true);
335	}
336}