allscreenshots_sdk/
error.rs1use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum ErrorCode {
8 ValidationError,
10 Unauthorized,
12 NotFound,
14 RateLimitExceeded,
16 InternalError,
18 Cancelled,
20 Timeout,
22 NetworkError,
24 Unknown(String),
26}
27
28impl From<&str> for ErrorCode {
29 fn from(s: &str) -> Self {
30 match s {
31 "VALIDATION_ERROR" => ErrorCode::ValidationError,
32 "UNAUTHORIZED" => ErrorCode::Unauthorized,
33 "NOT_FOUND" => ErrorCode::NotFound,
34 "RATE_LIMIT_EXCEEDED" => ErrorCode::RateLimitExceeded,
35 "INTERNAL_ERROR" => ErrorCode::InternalError,
36 "CANCELLED" => ErrorCode::Cancelled,
37 "TIMEOUT" => ErrorCode::Timeout,
38 "NETWORK_ERROR" => ErrorCode::NetworkError,
39 _ => ErrorCode::Unknown(s.to_string()),
40 }
41 }
42}
43
44impl std::fmt::Display for ErrorCode {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 ErrorCode::ValidationError => write!(f, "VALIDATION_ERROR"),
48 ErrorCode::Unauthorized => write!(f, "UNAUTHORIZED"),
49 ErrorCode::NotFound => write!(f, "NOT_FOUND"),
50 ErrorCode::RateLimitExceeded => write!(f, "RATE_LIMIT_EXCEEDED"),
51 ErrorCode::InternalError => write!(f, "INTERNAL_ERROR"),
52 ErrorCode::Cancelled => write!(f, "CANCELLED"),
53 ErrorCode::Timeout => write!(f, "TIMEOUT"),
54 ErrorCode::NetworkError => write!(f, "NETWORK_ERROR"),
55 ErrorCode::Unknown(s) => write!(f, "{}", s),
56 }
57 }
58}
59
60#[derive(Error, Debug)]
62pub enum AllscreenshotsError {
63 #[error("API error ({code}): {message}")]
65 ApiError {
66 code: ErrorCode,
68 message: String,
70 status: u16,
72 },
73
74 #[error("Validation error: {0}")]
76 ValidationError(String),
77
78 #[error("HTTP error: {0}")]
80 HttpError(#[from] reqwest::Error),
81
82 #[error("Invalid URL: {0}")]
84 UrlError(#[from] url::ParseError),
85
86 #[error("JSON error: {0}")]
88 JsonError(#[from] serde_json::Error),
89
90 #[error("Configuration error: {0}")]
92 ConfigError(String),
93
94 #[error("Environment variable '{0}' not set")]
96 EnvVarNotSet(String),
97
98 #[error("All retries exhausted: {0}")]
100 RetriesExhausted(String),
101
102 #[error("Request timeout")]
104 Timeout,
105}
106
107impl AllscreenshotsError {
108 pub fn is_retryable(&self) -> bool {
110 match self {
111 AllscreenshotsError::ApiError { code, status, .. } => {
112 matches!(code, ErrorCode::RateLimitExceeded | ErrorCode::InternalError)
113 || *status >= 500
114 }
115 AllscreenshotsError::HttpError(e) => e.is_timeout() || e.is_connect(),
116 AllscreenshotsError::Timeout => true,
117 _ => false,
118 }
119 }
120
121 pub fn from_api_response(status: u16, code: Option<&str>, message: &str) -> Self {
123 AllscreenshotsError::ApiError {
124 code: code.map(ErrorCode::from).unwrap_or(ErrorCode::Unknown("UNKNOWN".to_string())),
125 message: message.to_string(),
126 status,
127 }
128 }
129}
130
131#[derive(Debug, serde::Deserialize)]
133pub(crate) struct ApiErrorResponse {
134 #[serde(rename = "errorCode")]
135 pub error_code: Option<String>,
136 #[serde(rename = "errorMessage")]
137 pub error_message: Option<String>,
138 pub message: Option<String>,
139 pub error: Option<String>,
140}
141
142impl ApiErrorResponse {
143 pub fn get_message(&self) -> String {
145 self.error_message
146 .as_ref()
147 .or(self.message.as_ref())
148 .or(self.error.as_ref())
149 .cloned()
150 .unwrap_or_else(|| "Unknown error".to_string())
151 }
152}