Skip to main content

telos_agent/integrations/plugin/
errors.rs

1//! Plugin system error types — discriminated union following modular error-pattern conventions.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6use crate::integrations::plugin::PluginId;
7
8/// Why a dependency requirement was not satisfied.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum DependencyReason {
11    /// The dependency is installed but not enabled.
12    NotEnabled,
13    /// The dependency was not found in any configured marketplace.
14    NotFound,
15}
16
17impl std::fmt::Display for DependencyReason {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            DependencyReason::NotEnabled => write!(f, "not enabled"),
21            DependencyReason::NotFound => write!(f, "not found"),
22        }
23    }
24}
25
26/// All error conditions surfaced by the plugin system.
27#[derive(Debug, Clone, Error)]
28pub enum PluginError {
29    // --- Manifest ---
30    #[error("manifest not found at {path}")]
31    ManifestNotFound { path: PathBuf },
32    #[error("manifest parse error in {path}: {reason}")]
33    ManifestParse { path: PathBuf, reason: String },
34    #[error("manifest validation failed: {errors:?}")]
35    ManifestValidation { errors: Vec<String> },
36
37    // --- Sources ---
38    #[error("plugin '{plugin_id}' not found in marketplace '{marketplace}'")]
39    PluginNotFound { plugin_id: String, marketplace: String },
40    #[error("marketplace '{marketplace}' not found. Available: {available:?}")]
41    MarketplaceNotFound { marketplace: String, available: Vec<String> },
42    #[error("git clone failed for {url}: {reason}")]
43    GitCloneFailed { url: String, reason: String },
44    #[error("npm install failed for {package}: {reason}")]
45    NpmInstallFailed { package: String, reason: String },
46    #[error("pip install failed for {package}: {reason}")]
47    PipInstallFailed { package: String, reason: String },
48    #[error("network error fetching {url}: {detail}")]
49    NetworkError { url: String, detail: String },
50
51    // --- Dependencies ---
52    #[error("dependency '{dependency}' is {reason}")]
53    DependencyUnsatisfied { dependency: String, reason: DependencyReason },
54    #[error("circular dependency detected: {cycle:?}")]
55    CircularDependency { cycle: Vec<PluginId> },
56
57    // --- Lifecycle ---
58    #[error("plugin '{0}' is already enabled")]
59    AlreadyEnabled(PluginId),
60    #[error("plugin '{0}' is already disabled")]
61    AlreadyDisabled(PluginId),
62    #[error("plugin '{0}' failed to load components: {1}")]
63    ComponentLoadFailed(PluginId, String),
64    #[error("plugin '{id}' is degraded — {loaded}/{total} components loaded")]
65    Degraded { id: PluginId, loaded: usize, total: usize },
66
67    // --- User config ---
68    #[error("user configuration required for plugin '{id}'")]
69    UserConfigRequired { id: PluginId },
70    #[error("user configuration validation failed: {errors:?}")]
71    UserConfigValidation { errors: Vec<String> },
72
73    // --- I/O ---
74    #[error("I/O error: {0}")]
75    Io(String),
76
77    // --- Serde ---
78    #[error("JSON error: {0}")]
79    Json(String),
80
81    // --- Generic ---
82    #[error("{0}")]
83    Other(String),
84}
85
86impl From<std::io::Error> for PluginError {
87    fn from(err: std::io::Error) -> Self {
88        PluginError::Io(err.to_string())
89    }
90}
91
92impl From<serde_json::Error> for PluginError {
93    fn from(err: serde_json::Error) -> Self {
94        PluginError::Json(err.to_string())
95    }
96}