Skip to main content

telos_agent/integrations/plugin/registry/
lifecycle.rs

1//! Plugin registry lifecycle and query methods.
2
3use crate::integrations::plugin::registry::types::{LoadedPlugin, PluginEntry, PluginStatus};
4use crate::integrations::plugin::{PluginError, PluginId};
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8pub struct PluginRegistry {
9    pub(crate) plugins: HashMap<PluginId, PluginEntry>,
10    pub(crate) plugins_root: PathBuf,
11}
12
13impl PluginRegistry {
14    /// Create a new registry backed by `plugins_root` (typically `~/.telos/plugins/`).
15    pub fn new(plugins_root: impl Into<PathBuf>) -> Self {
16        Self { plugins: HashMap::new(), plugins_root: plugins_root.into() }
17    }
18    /// Path where installed plugins live.
19    pub fn installed_dir(&self) -> PathBuf {
20        self.plugins_root.join("installed")
21    }
22
23    /// Path to the state file.
24    pub fn state_path(&self) -> PathBuf {
25        self.plugins_root.join("plugin_state.json")
26    }
27    /// Register a loaded plugin without enabling it.
28    ///
29    /// If a plugin with the same ID already exists, it is replaced
30    /// (the old plugin's state is lost).
31    pub fn register(&mut self, plugin: LoadedPlugin) {
32        let status = if plugin.enabled { PluginStatus::Enabled } else { PluginStatus::Disabled };
33        self.plugins.insert(plugin.id.clone(), PluginEntry::new(plugin, status));
34    }
35    /// Enable a plugin. Call this after registration.
36    ///
37    /// This is idempotent — enabling an already-enabled plugin is a no-op.
38    pub fn enable(&mut self, id: &PluginId) -> Result<(), PluginError> {
39        let entry = self.plugins.get_mut(id).ok_or_else(|| PluginError::PluginNotFound {
40            plugin_id: id.to_string(),
41            marketplace: id.marketplace.clone(),
42        })?;
43
44        if entry.status == PluginStatus::Enabled {
45            return Ok(());
46        }
47
48        entry.status = PluginStatus::Enabled;
49        entry.plugin.enabled = true;
50        Ok(())
51    }
52    /// Disable a plugin. Does not uninstall — the plugin stays on disk.
53    ///
54    /// This is idempotent — disabling an already-disabled plugin is a no-op.
55    pub fn disable(&mut self, id: &PluginId) -> Result<(), PluginError> {
56        let entry = self.plugins.get_mut(id).ok_or_else(|| PluginError::PluginNotFound {
57            plugin_id: id.to_string(),
58            marketplace: id.marketplace.clone(),
59        })?;
60
61        if entry.status == PluginStatus::Disabled {
62            return Ok(());
63        }
64
65        entry.status = PluginStatus::Disabled;
66        entry.plugin.enabled = false;
67        Ok(())
68    }
69    /// Mark a plugin as degraded (enabled but with component load errors).
70    pub fn mark_degraded(&mut self, id: &PluginId, errors: Vec<PluginError>) {
71        if let Some(entry) = self.plugins.get_mut(id) {
72            entry.status = PluginStatus::Degraded;
73            entry.load_errors = errors;
74        }
75    }
76    /// Mark a plugin as in error state.
77    pub fn mark_error(&mut self, id: &PluginId, error: PluginError) {
78        if let Some(entry) = self.plugins.get_mut(id) {
79            entry.status = PluginStatus::Error;
80            entry.load_errors = vec![error];
81        }
82    }
83    /// Remove a plugin from the registry entirely.
84    pub fn remove(&mut self, id: &PluginId) -> Option<PluginEntry> {
85        self.plugins.remove(id)
86    }
87    /// Look up a plugin by ID.
88    pub fn get(&self, id: &PluginId) -> Option<&PluginEntry> {
89        self.plugins.get(id)
90    }
91    /// Mutable lookup.
92    pub fn get_mut(&mut self, id: &PluginId) -> Option<&mut PluginEntry> {
93        self.plugins.get_mut(id)
94    }
95    /// All enabled plugins.
96    pub fn list_enabled(&self) -> Vec<&PluginEntry> {
97        self.plugins
98            .values()
99            .filter(|e| e.status == PluginStatus::Enabled || e.status == PluginStatus::Degraded)
100            .collect()
101    }
102    /// All disabled plugins.
103    pub fn list_disabled(&self) -> Vec<&PluginEntry> {
104        self.plugins.values().filter(|e| e.status == PluginStatus::Disabled).collect()
105    }
106    /// All plugins regardless of status.
107    pub fn list_all(&self) -> Vec<&PluginEntry> {
108        self.plugins.values().collect()
109    }
110    /// Check if a plugin is installed (present in registry, any status).
111    pub fn is_installed(&self, id: &PluginId) -> bool {
112        self.plugins.contains_key(id)
113    }
114    /// Number of registered plugins.
115    pub fn len(&self) -> usize {
116        self.plugins.len()
117    }
118    /// Returns `true` if no plugins are registered.
119    pub fn is_empty(&self) -> bool {
120        self.plugins.is_empty()
121    }
122}