1pub type Result<T> = std::result::Result<T, PluginError>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum PluginError {
9 #[error("Plugin loading error: {message}")]
11 LoadError {
12 message: String,
14 },
15
16 #[error("Plugin execution error: {message}")]
18 ExecutionError {
19 message: String,
21 },
22
23 #[error("Security violation: {violation}")]
25 SecurityViolation {
26 violation: String,
28 },
29
30 #[error("Resource limit exceeded: {resource} limit={limit}, used={used}")]
32 ResourceLimitExceeded {
33 resource: String,
35 limit: String,
37 used: String,
39 },
40
41 #[error("Invalid plugin configuration: {field} - {message}")]
43 InvalidConfiguration {
44 field: String,
46 message: String,
48 },
49
50 #[error("Plugin compatibility error: {reason}")]
52 CompatibilityError {
53 reason: String,
55 },
56
57 #[error("Plugin communication error: {message}")]
59 CommunicationError {
60 message: String,
62 },
63
64 #[error("Plugin execution timeout: {timeout_ms}ms exceeded")]
66 TimeoutError {
67 timeout_ms: u64,
69 },
70
71 #[error("WebAssembly runtime error: {message}")]
73 WasmError {
74 message: String,
76 },
77
78 #[error("Invalid plugin manifest: {message}")]
80 InvalidManifest {
81 message: String,
83 },
84
85 #[error("Plugin dependency error: {dependency} - {message}")]
87 DependencyError {
88 dependency: String,
90 message: String,
92 },
93
94 #[error("Plugin system error: {message}")]
96 SystemError {
97 message: String,
99 },
100}
101
102impl PluginError {
103 pub fn load<S: Into<String>>(message: S) -> Self {
105 Self::LoadError {
106 message: message.into(),
107 }
108 }
109
110 pub fn execution<S: Into<String>>(message: S) -> Self {
112 Self::ExecutionError {
113 message: message.into(),
114 }
115 }
116
117 pub fn security<S: Into<String>>(violation: S) -> Self {
119 Self::SecurityViolation {
120 violation: violation.into(),
121 }
122 }
123
124 pub fn resource_limit<S: Into<String>>(resource: S, limit: S, used: S) -> Self {
126 Self::ResourceLimitExceeded {
127 resource: resource.into(),
128 limit: limit.into(),
129 used: used.into(),
130 }
131 }
132
133 pub fn config<S: Into<String>>(field: S, message: S) -> Self {
135 Self::InvalidConfiguration {
136 field: field.into(),
137 message: message.into(),
138 }
139 }
140
141 pub fn compatibility<S: Into<String>>(reason: S) -> Self {
143 Self::CompatibilityError {
144 reason: reason.into(),
145 }
146 }
147
148 pub fn communication<S: Into<String>>(message: S) -> Self {
150 Self::CommunicationError {
151 message: message.into(),
152 }
153 }
154
155 pub fn timeout(timeout_ms: u64) -> Self {
157 Self::TimeoutError { timeout_ms }
158 }
159
160 pub fn wasm<S: Into<String>>(message: S) -> Self {
162 Self::WasmError {
163 message: message.into(),
164 }
165 }
166
167 pub fn manifest<S: Into<String>>(message: S) -> Self {
169 Self::InvalidManifest {
170 message: message.into(),
171 }
172 }
173
174 pub fn dependency<S: Into<String>>(dependency: S, message: S) -> Self {
176 Self::DependencyError {
177 dependency: dependency.into(),
178 message: message.into(),
179 }
180 }
181
182 pub fn system<S: Into<String>>(message: S) -> Self {
184 Self::SystemError {
185 message: message.into(),
186 }
187 }
188
189 pub fn is_security_error(&self) -> bool {
191 matches!(self, PluginError::SecurityViolation { .. })
192 }
193
194 pub fn is_resource_error(&self) -> bool {
196 matches!(self, PluginError::ResourceLimitExceeded { .. })
197 }
198
199 pub fn is_timeout_error(&self) -> bool {
201 matches!(self, PluginError::TimeoutError { .. })
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
211 fn test_load_error() {
212 let err = PluginError::load("failed to load plugin");
213 assert!(matches!(err, PluginError::LoadError { .. }));
214 assert!(err.to_string().contains("failed to load plugin"));
215 }
216
217 #[test]
218 fn test_execution_error() {
219 let err = PluginError::execution("execution failed");
220 assert!(matches!(err, PluginError::ExecutionError { .. }));
221 assert!(err.to_string().contains("execution failed"));
222 }
223
224 #[test]
225 fn test_security_error() {
226 let err = PluginError::security("unauthorized access");
227 assert!(matches!(err, PluginError::SecurityViolation { .. }));
228 assert!(err.to_string().contains("unauthorized access"));
229 }
230
231 #[test]
232 fn test_resource_limit_error() {
233 let err = PluginError::resource_limit("memory", "100MB", "150MB");
234 assert!(matches!(err, PluginError::ResourceLimitExceeded { .. }));
235 let msg = err.to_string();
236 assert!(msg.contains("memory"));
237 assert!(msg.contains("100MB"));
238 assert!(msg.contains("150MB"));
239 }
240
241 #[test]
242 fn test_config_error() {
243 let err = PluginError::config("timeout", "must be positive");
244 assert!(matches!(err, PluginError::InvalidConfiguration { .. }));
245 let msg = err.to_string();
246 assert!(msg.contains("timeout"));
247 assert!(msg.contains("must be positive"));
248 }
249
250 #[test]
251 fn test_compatibility_error() {
252 let err = PluginError::compatibility("API version mismatch");
253 assert!(matches!(err, PluginError::CompatibilityError { .. }));
254 assert!(err.to_string().contains("API version mismatch"));
255 }
256
257 #[test]
258 fn test_communication_error() {
259 let err = PluginError::communication("connection refused");
260 assert!(matches!(err, PluginError::CommunicationError { .. }));
261 assert!(err.to_string().contains("connection refused"));
262 }
263
264 #[test]
265 fn test_timeout_error() {
266 let err = PluginError::timeout(5000);
267 assert!(matches!(err, PluginError::TimeoutError { timeout_ms: 5000 }));
268 assert!(err.to_string().contains("5000"));
269 }
270
271 #[test]
272 fn test_wasm_error() {
273 let err = PluginError::wasm("invalid wasm module");
274 assert!(matches!(err, PluginError::WasmError { .. }));
275 assert!(err.to_string().contains("invalid wasm module"));
276 }
277
278 #[test]
279 fn test_manifest_error() {
280 let err = PluginError::manifest("missing required field");
281 assert!(matches!(err, PluginError::InvalidManifest { .. }));
282 assert!(err.to_string().contains("missing required field"));
283 }
284
285 #[test]
286 fn test_dependency_error() {
287 let err = PluginError::dependency("plugin-foo", "version not found");
288 assert!(matches!(err, PluginError::DependencyError { .. }));
289 let msg = err.to_string();
290 assert!(msg.contains("plugin-foo"));
291 assert!(msg.contains("version not found"));
292 }
293
294 #[test]
295 fn test_system_error() {
296 let err = PluginError::system("internal failure");
297 assert!(matches!(err, PluginError::SystemError { .. }));
298 assert!(err.to_string().contains("internal failure"));
299 }
300
301 #[test]
303 fn test_is_security_error() {
304 let err = PluginError::security("test");
305 assert!(err.is_security_error());
306
307 let err = PluginError::load("test");
308 assert!(!err.is_security_error());
309 }
310
311 #[test]
312 fn test_is_resource_error() {
313 let err = PluginError::resource_limit("memory", "100MB", "150MB");
314 assert!(err.is_resource_error());
315
316 let err = PluginError::execution("test");
317 assert!(!err.is_resource_error());
318 }
319
320 #[test]
321 fn test_is_timeout_error() {
322 let err = PluginError::timeout(1000);
323 assert!(err.is_timeout_error());
324
325 let err = PluginError::wasm("test");
326 assert!(!err.is_timeout_error());
327 }
328
329 #[test]
331 fn test_load_error_display() {
332 let err = PluginError::LoadError {
333 message: "test message".to_string(),
334 };
335 assert_eq!(err.to_string(), "Plugin loading error: test message");
336 }
337
338 #[test]
339 fn test_execution_error_display() {
340 let err = PluginError::ExecutionError {
341 message: "runtime failure".to_string(),
342 };
343 assert_eq!(err.to_string(), "Plugin execution error: runtime failure");
344 }
345
346 #[test]
347 fn test_security_violation_display() {
348 let err = PluginError::SecurityViolation {
349 violation: "access denied".to_string(),
350 };
351 assert_eq!(err.to_string(), "Security violation: access denied");
352 }
353
354 #[test]
355 fn test_resource_limit_display() {
356 let err = PluginError::ResourceLimitExceeded {
357 resource: "CPU".to_string(),
358 limit: "50%".to_string(),
359 used: "75%".to_string(),
360 };
361 assert_eq!(err.to_string(), "Resource limit exceeded: CPU limit=50%, used=75%");
362 }
363
364 #[test]
365 fn test_invalid_config_display() {
366 let err = PluginError::InvalidConfiguration {
367 field: "port".to_string(),
368 message: "invalid value".to_string(),
369 };
370 assert_eq!(err.to_string(), "Invalid plugin configuration: port - invalid value");
371 }
372
373 #[test]
374 fn test_timeout_display() {
375 let err = PluginError::TimeoutError { timeout_ms: 3000 };
376 assert_eq!(err.to_string(), "Plugin execution timeout: 3000ms exceeded");
377 }
378
379 #[test]
380 fn test_dependency_error_display() {
381 let err = PluginError::DependencyError {
382 dependency: "core-lib".to_string(),
383 message: "not found".to_string(),
384 };
385 assert_eq!(err.to_string(), "Plugin dependency error: core-lib - not found");
386 }
387
388 #[test]
390 fn test_error_debug() {
391 let err = PluginError::load("test");
392 let debug = format!("{:?}", err);
393 assert!(debug.contains("LoadError"));
394 }
395
396 #[test]
398 fn test_load_error_from_string() {
399 let err = PluginError::load(String::from("dynamic message"));
400 assert!(err.to_string().contains("dynamic message"));
401 }
402
403 #[test]
404 fn test_load_error_from_str() {
405 let err = PluginError::load("static message");
406 assert!(err.to_string().contains("static message"));
407 }
408}