use std::collections::HashMap;
use tokio::process::Child;
use crate::domain::plugin::{PluginId, PluginInfo};
use stynx_code_errors::{AppError, AppResult};
pub struct PluginLifecycleManager {
processes: HashMap<PluginId, Child>,
}
impl PluginLifecycleManager {
pub fn new() -> Self {
Self {
processes: HashMap::new(),
}
}
pub async fn start(&mut self, info: &PluginInfo) -> AppResult<()> {
if self.processes.contains_key(&info.id) {
return Err(AppError::BadRequest(format!(
"Plugin '{}' is already running",
info.id
)));
}
let entry_point = info.path.join("run");
let child = tokio::process::Command::new(&entry_point)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()
.map_err(|e| {
AppError::Internal(anyhow::anyhow!(
"Failed to spawn plugin '{}' at {}: {e}",
info.id,
entry_point.display()
))
})?;
self.processes.insert(info.id.clone(), child);
Ok(())
}
pub async fn stop(&mut self, id: &PluginId) -> AppResult<()> {
let child = self.processes.get_mut(id).ok_or_else(|| {
AppError::BadRequest(format!("Plugin '{}' is not running", id))
})?;
child.kill().await.map_err(|e| {
AppError::Internal(anyhow::anyhow!("Failed to kill plugin '{}': {e}", id))
})?;
self.processes.remove(id);
Ok(())
}
pub fn health_check(&mut self, id: &PluginId) -> bool {
match self.processes.get_mut(id) {
None => false,
Some(child) => matches!(child.try_wait(), Ok(None)),
}
}
}
impl Default for PluginLifecycleManager {
fn default() -> Self {
Self::new()
}
}