use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum AfricasTalkingError {
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("API error: {message} (code: {code})")]
Api {
message: String,
code: String,
more_info: Option<String>,
},
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("Authentication error: {0}")]
Auth(String),
#[error("Rate limit exceeded. Try again after {retry_after} seconds")]
RateLimit { retry_after: u64 },
#[error("Request timeout")]
Timeout,
#[error("Internal error: {0}")]
Internal(String),
}
pub type Result<T> = std::result::Result<T, AfricasTalkingError>;
#[derive(Debug, Deserialize, Serialize)]
pub struct ApiErrorResponse {
#[serde(rename = "ErrorMessage")]
pub error_message: String,
#[serde(rename = "ErrorCode")]
pub error_code: Option<String>,
#[serde(rename = "MoreInfo")]
pub more_info: Option<String>,
}
impl AfricasTalkingError {
pub fn api_error(message: String, code: String, more_info: Option<String>) -> Self {
Self::Api {
message,
code,
more_info,
}
}
pub fn validation<S: Into<String>>(message: S) -> Self {
Self::Validation(message.into())
}
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Config(message.into())
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
AfricasTalkingError::Http(_)
| AfricasTalkingError::Timeout
| AfricasTalkingError::RateLimit { .. }
)
}
}