fusabi_plugin_runtime/
error.rs1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Error, Debug)]
10pub enum Error {
11 #[error("plugin not found: {0}")]
13 PluginNotFound(String),
14
15 #[error("plugin already loaded: {0}")]
17 PluginAlreadyLoaded(String),
18
19 #[error("invalid manifest: {0}")]
21 InvalidManifest(String),
22
23 #[error("missing required manifest field: {0}")]
25 MissingManifestField(String),
26
27 #[error("API version mismatch: plugin requires {required}, host provides {provided}")]
29 ApiVersionMismatch {
30 required: String,
32 provided: String,
34 },
35
36 #[error("missing required capability: {0}")]
38 MissingCapability(String),
39
40 #[error("capability not declared in manifest: {0}")]
42 UndeclaredCapability(String),
43
44 #[error("dependency not satisfied: {name} requires {version}")]
46 DependencyNotSatisfied {
47 name: String,
49 version: String,
51 },
52
53 #[error("plugin initialization failed: {0}")]
55 InitializationFailed(String),
56
57 #[error("plugin execution failed: {0}")]
59 ExecutionFailed(String),
60
61 #[error("invalid plugin state: expected {expected}, got {actual}")]
63 InvalidState {
64 expected: String,
66 actual: String,
68 },
69
70 #[error("function not found: {0}")]
72 FunctionNotFound(String),
73
74 #[error("compilation error: {0}")]
76 Compilation(String),
77
78 #[error("io error: {0}")]
80 Io(#[from] std::io::Error),
81
82 #[error("host error: {0}")]
84 Host(#[from] fusabi_host::Error),
85
86 #[cfg(feature = "serde")]
88 #[error("manifest parse error: {0}")]
89 ManifestParse(String),
90
91 #[cfg(feature = "watch")]
93 #[error("watch error: {0}")]
94 Watch(String),
95
96 #[error("plugin was unloaded")]
98 PluginUnloaded,
99
100 #[error("plugin reload failed: {0}")]
102 ReloadFailed(String),
103
104 #[error("registry error: {0}")]
106 Registry(String),
107}
108
109impl Error {
110 pub fn plugin_not_found(name: impl Into<String>) -> Self {
112 Self::PluginNotFound(name.into())
113 }
114
115 pub fn invalid_manifest(msg: impl Into<String>) -> Self {
117 Self::InvalidManifest(msg.into())
118 }
119
120 pub fn missing_field(field: impl Into<String>) -> Self {
122 Self::MissingManifestField(field.into())
123 }
124
125 pub fn api_version_mismatch(required: impl Into<String>, provided: impl Into<String>) -> Self {
127 Self::ApiVersionMismatch {
128 required: required.into(),
129 provided: provided.into(),
130 }
131 }
132
133 pub fn missing_capability(cap: impl Into<String>) -> Self {
135 Self::MissingCapability(cap.into())
136 }
137
138 pub fn dependency_not_satisfied(name: impl Into<String>, version: impl Into<String>) -> Self {
140 Self::DependencyNotSatisfied {
141 name: name.into(),
142 version: version.into(),
143 }
144 }
145
146 pub fn init_failed(msg: impl Into<String>) -> Self {
148 Self::InitializationFailed(msg.into())
149 }
150
151 pub fn execution_failed(msg: impl Into<String>) -> Self {
153 Self::ExecutionFailed(msg.into())
154 }
155
156 pub fn invalid_state(expected: impl Into<String>, actual: impl Into<String>) -> Self {
158 Self::InvalidState {
159 expected: expected.into(),
160 actual: actual.into(),
161 }
162 }
163
164 pub fn is_recoverable(&self) -> bool {
166 matches!(
167 self,
168 Self::PluginNotFound(_)
169 | Self::FunctionNotFound(_)
170 | Self::InvalidState { .. }
171 )
172 }
173
174 pub fn should_reload(&self) -> bool {
176 matches!(
177 self,
178 Self::Compilation(_) | Self::ExecutionFailed(_) | Self::ReloadFailed(_)
179 )
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_error_display() {
189 let err = Error::plugin_not_found("my-plugin");
190 assert_eq!(err.to_string(), "plugin not found: my-plugin");
191
192 let err = Error::api_version_mismatch("0.2.0", "0.1.0");
193 assert!(err.to_string().contains("0.2.0"));
194 assert!(err.to_string().contains("0.1.0"));
195 }
196
197 #[test]
198 fn test_error_classification() {
199 assert!(Error::plugin_not_found("test").is_recoverable());
200 assert!(!Error::init_failed("test").is_recoverable());
201
202 assert!(Error::Compilation("test".into()).should_reload());
203 assert!(!Error::plugin_not_found("test").should_reload());
204 }
205}