use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PayrixApiError {
pub msg: String,
#[serde(default)]
pub field: Option<String>,
#[serde(default)]
pub code: Option<i32>,
#[serde(default)]
pub error_code: Option<String>,
#[serde(default)]
pub severity: Option<i32>,
}
impl fmt::Display for PayrixApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)?;
if let Some(field) = &self.field {
write!(f, " (field: {})", field)?;
}
if let Some(code) = &self.error_code {
write!(f, " [{}]", code)?;
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Rate limited: {0}")]
RateLimited(String),
#[error("Payrix API error: {}", format_api_errors(.0))]
Api(Vec<PayrixApiError>),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(String),
#[error("Bad request: {0}")]
BadRequest(String),
#[error("Unauthorized: {0}")]
Unauthorized(String),
#[error("Service unavailable: {0}")]
ServiceUnavailable(String),
#[error("Unprocessable entity: {0}")]
UnprocessableEntity(String),
#[error("Internal error: {0}")]
Internal(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("IO error: {0}")]
Io(String),
#[cfg(feature = "cache")]
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
#[error("Workflow error: {0}")]
Workflow(String),
}
fn format_api_errors(errors: &[PayrixApiError]) -> String {
errors
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("; ")
}
impl Error {
pub fn is_retryable(&self) -> bool {
matches!(
self,
Error::RateLimited(_) | Error::ServiceUnavailable(_) | Error::Http(_)
)
}
pub fn from_api_errors(errors: Vec<PayrixApiError>) -> Self {
Error::Api(errors)
}
}
pub type Result<T> = std::result::Result<T, Error>;