pipedash_plugin_api/
error.rs1use std::time::Duration;
2
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6#[non_exhaustive]
7pub enum PluginError {
8 #[error("Authentication failed: {0}")]
9 AuthenticationFailed(String),
10
11 #[error("Permission denied: {0}")]
12 PermissionDenied(String),
13
14 #[error("API error: {0}")]
15 ApiError(String),
16
17 #[error("Invalid configuration: {0}")]
18 InvalidConfig(String),
19
20 #[error("Pipeline not found: {0}")]
21 PipelineNotFound(String),
22
23 #[error("Provider not supported: {0}")]
24 ProviderNotSupported(String),
25
26 #[error("Network error: {0}")]
27 NetworkError(String),
28
29 #[error("Serialization error: {0}")]
30 SerializationError(String),
31
32 #[error("Database error: {0}")]
33 DatabaseError(String),
34
35 #[error("Operation not supported: {0}")]
36 NotSupported(String),
37
38 #[error("Internal error: {0}")]
39 Internal(String),
40
41 #[error("Rate limited: {message}")]
42 RateLimited {
43 message: String,
44 retry_after: Option<Duration>,
45 },
46
47 #[error("Service unavailable: {message}")]
48 ServiceUnavailable {
49 message: String,
50 retry_after: Option<Duration>,
51 },
52
53 #[error("TLS error: {0}")]
54 Tls(String),
55}
56
57pub type PluginResult<T> = Result<T, PluginError>;
58
59impl PluginError {
60 pub fn is_transient(&self) -> bool {
61 matches!(
62 self,
63 Self::NetworkError(_) | Self::ServiceUnavailable { .. } | Self::RateLimited { .. }
64 )
65 }
66
67 pub fn retry_after(&self) -> Option<Duration> {
68 match self {
69 Self::RateLimited { retry_after, .. }
70 | Self::ServiceUnavailable { retry_after, .. } => *retry_after,
71 _ => None,
72 }
73 }
74}
75
76impl From<serde_json::Error> for PluginError {
77 fn from(err: serde_json::Error) -> Self {
78 PluginError::SerializationError(err.to_string())
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn transient_covers_network_unavailable_and_rate_limit_only() {
88 assert!(PluginError::NetworkError("x".into()).is_transient());
89 assert!(PluginError::RateLimited {
90 message: "x".into(),
91 retry_after: None
92 }
93 .is_transient());
94 assert!(PluginError::ServiceUnavailable {
95 message: "x".into(),
96 retry_after: None
97 }
98 .is_transient());
99 assert!(!PluginError::ApiError("x".into()).is_transient());
100 assert!(!PluginError::AuthenticationFailed("x".into()).is_transient());
101 assert!(!PluginError::Tls("x".into()).is_transient());
102 }
103}