Skip to main content

stynx_code_plugins/application/
lifecycle.rs

1use std::collections::HashMap;
2
3use tokio::process::Child;
4
5use crate::domain::plugin::{PluginId, PluginInfo};
6use stynx_code_errors::{AppError, AppResult};
7
8pub struct PluginLifecycleManager {
9    processes: HashMap<PluginId, Child>,
10}
11
12impl PluginLifecycleManager {
13    pub fn new() -> Self {
14        Self {
15            processes: HashMap::new(),
16        }
17    }
18
19    pub async fn start(&mut self, info: &PluginInfo) -> AppResult<()> {
20        if self.processes.contains_key(&info.id) {
21            return Err(AppError::BadRequest(format!(
22                "Plugin '{}' is already running",
23                info.id
24            )));
25        }
26
27        let entry_point = info.path.join("run");
28        let child = tokio::process::Command::new(&entry_point)
29            .stdin(std::process::Stdio::piped())
30            .stdout(std::process::Stdio::piped())
31            .stderr(std::process::Stdio::null())
32            .spawn()
33            .map_err(|e| {
34                AppError::Internal(anyhow::anyhow!(
35                    "Failed to spawn plugin '{}' at {}: {e}",
36                    info.id,
37                    entry_point.display()
38                ))
39            })?;
40
41        self.processes.insert(info.id.clone(), child);
42        Ok(())
43    }
44
45    pub async fn stop(&mut self, id: &PluginId) -> AppResult<()> {
46        let child = self.processes.get_mut(id).ok_or_else(|| {
47            AppError::BadRequest(format!("Plugin '{}' is not running", id))
48        })?;
49
50        child.kill().await.map_err(|e| {
51            AppError::Internal(anyhow::anyhow!("Failed to kill plugin '{}': {e}", id))
52        })?;
53
54        self.processes.remove(id);
55        Ok(())
56    }
57
58    pub fn health_check(&mut self, id: &PluginId) -> bool {
59        match self.processes.get_mut(id) {
60            None => false,
61            Some(child) => matches!(child.try_wait(), Ok(None)),
62        }
63    }
64}
65
66impl Default for PluginLifecycleManager {
67    fn default() -> Self {
68        Self::new()
69    }
70}