1use std::{collections::HashMap, path::PathBuf, sync::Arc};
7
8use anyhow::{Context, Result};
9use serde::{Deserialize, Serialize};
10use tokio::sync::RwLock;
11use tracing::{debug, info, instrument, warn};
12
13use crate::{
14 Common::traits::ExtensionContext,
15 Host::{ActivationResult, HostConfig},
16 Host::ExtensionManager::{ExtensionManagerImpl, ExtensionState, ExtensionInfo},
17};
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
21pub enum ActivationEvent {
22 Startup,
24 Command(String),
26 Language(String),
28 WorkspaceContains(String),
30 OnView(String),
32 OnUri(String),
34 OnFiles(String),
36 Custom(String),
38 Star,
40}
41
42impl ActivationEvent {
43 pub fn from_str(event_str:&str) -> Result<Self> {
45 match event_str {
46 "*" => Ok(Self::Star),
47 e if e.starts_with("onCommand:") => Ok(Self::Command(e.trim_start_matches("onCommand:").to_string())),
48 e if e.starts_with("onLanguage:") => Ok(Self::Language(e.trim_start_matches("onLanguage:").to_string())),
49 e if e.starts_with("workspaceContains:") => {
50 Ok(Self::WorkspaceContains(e.trim_start_matches("workspaceContains:").to_string()))
51 },
52 e if e.starts_with("onView:") => Ok(Self::OnView(e.trim_start_matches("onView:").to_string())),
53 e if e.starts_with("onUri:") => Ok(Self::OnUri(e.trim_start_matches("onUri:").to_string())),
54 e if e.starts_with("onFiles:") => Ok(Self::OnFiles(e.trim_start_matches("onFiles:").to_string())),
55 _ => Ok(Self::Custom(event_str.to_string())),
56 }
57 }
58
59 pub fn to_string(&self) -> String {
61 match self {
62 Self::Startup => "onStartup".to_string(),
63 Self::Star => "*".to_string(),
64 Self::Command(cmd) => format!("onCommand:{}", cmd),
65 Self::Language(lang) => format!("onLanguage:{}", lang),
66 Self::WorkspaceContains(pattern) => format!("workspaceContains:{}", pattern),
67 Self::OnView(view) => format!("onView:{}", view),
68 Self::OnUri(uri) => format!("onUri:{}", uri),
69 Self::OnFiles(pattern) => format!("onFiles:{}", pattern),
70 Self::Custom(s) => s.clone(),
71 }
72 }
73}
74
75impl std::str::FromStr for ActivationEvent {
76 type Err = anyhow::Error;
77
78 fn from_str(s:&str) -> Result<Self, Self::Err> { Self::from_str(s) }
79}
80
81pub struct ActivationEngine {
83 extension_manager:Arc<ExtensionManagerImpl>,
85 config:HostConfig,
87 event_handlers:Arc<RwLock<HashMap<String, ActivationHandler>>>,
89 activation_history:Arc<RwLock<Vec<ActivationRecord>>>,
91}
92
93#[derive(Debug, Clone)]
95struct ActivationHandler {
96 extension_id:String,
98 events:Vec<ActivationEvent>,
100 activation_function:String,
102 is_active:bool,
104 last_activation:Option<u64>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ActivationRecord {
111 pub extension_id:String,
113 pub events:Vec<String>,
115 pub timestamp:u64,
117 pub duration_ms:u64,
119 pub success:bool,
121 pub error:Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ActivationContext {
128 pub workspace_path:Option<PathBuf>,
130 pub current_file:Option<PathBuf>,
132 pub language_id:Option<String>,
134 pub active_editor:bool,
136 pub environment:HashMap<String, String>,
138 pub additional_data:serde_json::Value,
140}
141
142impl Default for ActivationContext {
143 fn default() -> Self {
144 Self {
145 workspace_path:None,
146 current_file:None,
147 language_id:None,
148 active_editor:false,
149 environment:HashMap::new(),
150 additional_data:serde_json::Value::Null,
151 }
152 }
153}
154
155impl ActivationEngine {
156 pub fn new(extension_manager:Arc<ExtensionManagerImpl>, config:HostConfig) -> Self {
158 Self {
159 extension_manager,
160 config,
161 event_handlers:Arc::new(RwLock::new(HashMap::new())),
162 activation_history:Arc::new(RwLock::new(Vec::new())),
163 }
164 }
165
166 #[instrument(skip(self, extension_id))]
168 pub async fn activate(&self, extension_id:&str) -> Result<ActivationResult> {
169 info!("Activating extension: {}", extension_id);
170
171 let start = std::time::Instant::now();
172
173 let extension_info = self
175 .extension_manager
176 .get_extension(extension_id)
177 .await
178 .ok_or_else(|| anyhow::anyhow!("Extension not found: {}", extension_id))?;
179
180 let handlers = self.event_handlers.read().await;
182 if let Some(handler) = handlers.get(extension_id) {
183 if handler.is_active {
184 warn!("Extension already active: {}", extension_id);
185 return Ok(ActivationResult {
186 extension_id:extension_id.to_string(),
187 success:true,
188 time_ms:0,
189 error:None,
190 contributes:Vec::new(),
191 });
192 }
193 }
194 drop(handlers);
195
196 let activation_events:Result<Vec<ActivationEvent>> = extension_info
198 .activation_events
199 .iter()
200 .map(|e| ActivationEvent::from_str(e))
201 .collect();
202 let activation_events = activation_events.with_context(|| "Failed to parse activation events")?;
203
204 let context = ActivationContext::default();
206
207 let activation_result = self
210 .perform_activation(extension_id, &context)
211 .await
212 .context("Activation failed")?;
213
214 let elapsed_ms = start.elapsed().as_millis() as u64;
215
216 let record = ActivationRecord {
218 extension_id:extension_id.to_string(),
219 events:extension_info.activation_events.clone(),
220 timestamp:std::time::SystemTime::now()
221 .duration_since(std::time::UNIX_EPOCH)
222 .map(|d| d.as_secs())
223 .unwrap_or(0),
224 duration_ms:elapsed_ms,
225 success:activation_result.success,
226 error:None,
227 };
228
229 let activation_timestamp = record.timestamp;
231
232 self.activation_history.write().await.push(record);
233
234 self.extension_manager
236 .update_state(extension_id, ExtensionState::Activated)
237 .await?;
238
239 let mut handlers = self.event_handlers.write().await;
241 handlers.insert(
242 extension_id.to_string(),
243 ActivationHandler {
244 extension_id:extension_id.to_string(),
245 events:activation_events,
246 activation_function:"activate".to_string(),
247 is_active:true,
248 last_activation:Some(activation_timestamp),
249 },
250 );
251
252 info!("Extension activated in {}ms: {}", elapsed_ms, extension_id);
253
254 Ok(ActivationResult {
255 extension_id:extension_id.to_string(),
256 success:true,
257 time_ms:elapsed_ms,
258 error:None,
259 contributes:extension_info.capabilities.clone(),
260 })
261 }
262
263 #[instrument(skip(self, extension_id))]
265 pub async fn deactivate(&self, extension_id:&str) -> Result<()> {
266 info!("Deactivating extension: {}", extension_id);
267
268 let mut handlers = self.event_handlers.write().await;
270 if let Some(mut handler) = handlers.remove(extension_id) {
271 handler.is_active = false;
272 }
273
274 self.extension_manager
276 .update_state(extension_id, ExtensionState::Deactivated)
277 .await?;
278
279 info!("Extension deactivated: {}", extension_id);
280
281 Ok(())
282 }
283
284 #[instrument(skip(self, event, context))]
286 pub async fn trigger_activation(&self, event:&str, context:&ActivationContext) -> Result<Vec<ActivationResult>> {
287 info!("Triggering activation for event: {}", event);
288
289 let activation_event = ActivationEvent::from_str(event)?;
290 let handlers = self.event_handlers.read().await;
291
292 let mut results = Vec::new();
293
294 for (extension_id, handler) in handlers.iter() {
295 if handler.is_active {
297 continue; }
299
300 if self.should_activate(&activation_event, &handler.events) {
301 debug!("Activating extension {} for event: {}", extension_id, event);
302 match self.activate(extension_id).await {
303 Ok(result) => results.push(result),
304 Err(e) => {
305 warn!("Failed to activate extension {} for event {}: {}", extension_id, event, e);
306 },
307 }
308 }
309 }
310
311 Ok(results)
312 }
313
314 fn should_activate(&self, activation_event:&ActivationEvent, events:&[ActivationEvent]) -> bool {
316 events.iter().any(|e| {
317 match (e, activation_event) {
318 (ActivationEvent::Star, _) => true,
319 (ActivationEvent::Custom(pattern), _) => {
320 WildMatch::new(pattern).matches(activation_event.to_string().as_str())
321 },
322 _ => e == activation_event,
323 }
324 })
325 }
326
327 async fn perform_activation(&self, extension_id:&str, context:&ActivationContext) -> Result<ActivationResult> {
330 debug!("Performing activation for extension: {}", extension_id);
337
338 Ok(ActivationResult {
340 extension_id:extension_id.to_string(),
341 success:true,
342 time_ms:0,
343 error:None,
344 contributes:Vec::new(),
345 })
346 }
347
348 pub async fn get_activation_history(&self) -> Vec<ActivationRecord> { self.activation_history.read().await.clone() }
350
351 pub async fn get_activation_history_for_extension(&self, extension_id:&str) -> Vec<ActivationRecord> {
353 self.activation_history
354 .read()
355 .await
356 .iter()
357 .filter(|r| r.extension_id == extension_id)
358 .cloned()
359 .collect()
360 }
361}
362
363struct WildMatch {
365 pattern:String,
366}
367
368impl WildMatch {
369 fn new(pattern:&str) -> Self { Self { pattern:pattern.to_lowercase() } }
370
371 fn matches(&self, text:&str) -> bool {
372 let text = text.to_lowercase();
373
374 if self.pattern == "*" {
376 return true;
377 }
378
379 if self.pattern.starts_with('*') {
381 let suffix = &self.pattern[1..];
382 return text.ends_with(suffix);
383 }
384
385 if self.pattern.ends_with('*') {
387 let prefix = &self.pattern[..self.pattern.len() - 1];
388 return text.starts_with(prefix);
389 }
390
391 self.pattern == text
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn test_activation_event_parsing() {
402 let event = ActivationEvent::from_str("*").unwrap();
403 assert_eq!(event, ActivationEvent::Star);
404
405 let event = ActivationEvent::from_str("onCommand:test.command").unwrap();
406 assert_eq!(event, ActivationEvent::Command("test.command".to_string()));
407
408 let event = ActivationEvent::from_str("onLanguage:rust").unwrap();
409 assert_eq!(event, ActivationEvent::Language("rust".to_string()));
410 }
411
412 #[test]
413 fn test_activation_event_to_string() {
414 assert_eq!(ActivationEvent::Star.to_string(), "*");
415 assert_eq!(ActivationEvent::Command("test".to_string()).to_string(), "onCommand:test");
416 assert_eq!(ActivationEvent::Language("rust".to_string()).to_string(), "onLanguage:rust");
417 }
418
419 #[test]
420 fn test_activation_context_default() {
421 let context = ActivationContext::default();
422 assert!(context.workspace_path.is_none());
423 assert!(context.current_file.is_none());
424 assert!(!context.active_editor);
425 }
426
427 #[test]
428 fn test_wildcard_matching() {
429 let matcher = WildMatch::new("*");
430 assert!(matcher.matches("anything"));
431
432 let matcher = WildMatch::new("prefix*");
433 assert!(matcher.matches("prefix_suffix"));
434 assert!(!matcher.matches("noprefix_suffix"));
435
436 let matcher = WildMatch::new("*suffix");
437 assert!(matcher.matches("prefix_suffix"));
438 assert!(!matcher.matches("prefix_suffix_not"));
439 }
440}