Skip to main content

clawft_plugin/
error.rs

1//! Plugin error types.
2//!
3//! Defines [`PluginError`], the unified error type for all plugin operations
4//! including loading, execution, permission checks, and resource limits.
5
6use thiserror::Error;
7
8/// Errors produced by plugin operations.
9#[non_exhaustive]
10#[derive(Debug, Error)]
11pub enum PluginError {
12    /// Plugin failed to load (bad manifest, missing WASM module, etc.).
13    #[error("plugin load failed: {0}")]
14    LoadFailed(String),
15
16    /// Plugin execution failed at runtime.
17    #[error("plugin execution failed: {0}")]
18    ExecutionFailed(String),
19
20    /// Operation denied by the permission sandbox.
21    #[error("permission denied: {0}")]
22    PermissionDenied(String),
23
24    /// Plugin exceeded a resource limit (fuel, memory, rate limit).
25    #[error("resource exhausted: {0}")]
26    ResourceExhausted(String),
27
28    /// Requested capability or feature is not implemented.
29    #[error("not implemented: {0}")]
30    NotImplemented(String),
31
32    /// I/O error during plugin operation.
33    #[error("I/O error: {0}")]
34    Io(#[from] std::io::Error),
35
36    /// Serialization/deserialization error.
37    #[error("serialization error: {0}")]
38    Serialization(#[from] serde_json::Error),
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn error_display_load_failed() {
47        let err = PluginError::LoadFailed("bad manifest".into());
48        assert_eq!(err.to_string(), "plugin load failed: bad manifest");
49    }
50
51    #[test]
52    fn error_display_execution_failed() {
53        let err = PluginError::ExecutionFailed("runtime crash".into());
54        assert_eq!(err.to_string(), "plugin execution failed: runtime crash");
55    }
56
57    #[test]
58    fn error_display_permission_denied() {
59        let err = PluginError::PermissionDenied("network access".into());
60        assert_eq!(err.to_string(), "permission denied: network access");
61    }
62
63    #[test]
64    fn error_display_resource_exhausted() {
65        let err = PluginError::ResourceExhausted("fuel limit".into());
66        assert_eq!(err.to_string(), "resource exhausted: fuel limit");
67    }
68
69    #[test]
70    fn error_display_not_implemented() {
71        let err = PluginError::NotImplemented("voice processing".into());
72        assert_eq!(err.to_string(), "not implemented: voice processing");
73    }
74
75    #[test]
76    fn error_from_io() {
77        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
78        let err = PluginError::from(io_err);
79        assert!(matches!(err, PluginError::Io(_)));
80        assert!(err.to_string().contains("file missing"));
81    }
82
83    #[test]
84    fn error_from_serde_json() {
85        let json_err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
86        let err = PluginError::from(json_err);
87        assert!(matches!(err, PluginError::Serialization(_)));
88    }
89
90    #[test]
91    fn all_seven_variants_exist() {
92        // Compile-time verification that all 7 variants exist and are constructable.
93        let _variants: Vec<PluginError> = vec![
94            PluginError::LoadFailed(String::new()),
95            PluginError::ExecutionFailed(String::new()),
96            PluginError::PermissionDenied(String::new()),
97            PluginError::ResourceExhausted(String::new()),
98            PluginError::NotImplemented(String::new()),
99            PluginError::Io(std::io::Error::new(std::io::ErrorKind::Other, "")),
100            PluginError::Serialization(
101                serde_json::from_str::<serde_json::Value>("!").unwrap_err(),
102            ),
103        ];
104        assert_eq!(_variants.len(), 7);
105    }
106}