use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Http(reqwest::Error),
Api(ApiError),
Json(serde_json::Error),
NotFound(String),
InvalidInput(String),
Unauthorized(String),
RateLimited { retry_after: Option<u64> },
}
#[derive(Debug, Clone)]
pub struct ApiError {
pub status: u16,
pub code: Option<i32>,
pub message: String,
pub body: String,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Http(e) => write!(f, "HTTP request failed: {}", e),
Error::Api(e) => write!(f, "Cloudflare API error: {}", e.message),
Error::Json(e) => write!(f, "JSON serialization error: {}", e),
Error::NotFound(msg) => write!(f, "Resource not found: {}", msg),
Error::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
Error::Unauthorized(msg) => write!(f, "Authentication failed: {}", msg),
Error::RateLimited { retry_after } => {
if let Some(seconds) = retry_after {
write!(f, "Rate limited, retry after {} seconds", seconds)
} else {
write!(f, "Rate limited by Cloudflare API")
}
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Http(e) => Some(e),
Error::Json(e) => Some(e),
_ => None,
}
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::Http(err)
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Json(err)
}
}
impl ApiError {
pub fn new(status: u16, message: String, body: String) -> Self {
Self {
status,
code: None,
message,
body,
}
}
pub(crate) fn from_response(status: u16, body: &str) -> Self {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(body)
&& let Some(errors) = json["errors"].as_array()
&& let Some(first_error) = errors.first()
{
return Self {
status,
code: first_error["code"].as_i64().map(|c| c as i32),
message: first_error["message"]
.as_str()
.unwrap_or("Unknown error")
.to_string(),
body: body.to_string(),
};
}
Self {
status,
code: None,
message: format!("HTTP {}", status),
body: body.to_string(),
}
}
}