1use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug)]
10pub enum Error {
11 Http(reqwest::Error),
13
14 Api(ApiError),
16
17 Json(serde_json::Error),
19
20 NotFound(String),
22
23 InvalidInput(String),
25
26 Unauthorized(String),
28
29 RateLimited { retry_after: Option<u64> },
31}
32
33#[derive(Debug, Clone)]
35pub struct ApiError {
36 pub status: u16,
38
39 pub code: Option<i32>,
41
42 pub message: String,
44
45 pub body: String,
47}
48
49impl fmt::Display for Error {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Error::Http(e) => write!(f, "HTTP request failed: {}", e),
53 Error::Api(e) => write!(f, "Cloudflare API error: {}", e.message),
54 Error::Json(e) => write!(f, "JSON serialization error: {}", e),
55 Error::NotFound(msg) => write!(f, "Resource not found: {}", msg),
56 Error::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
57 Error::Unauthorized(msg) => write!(f, "Authentication failed: {}", msg),
58 Error::RateLimited { retry_after } => {
59 if let Some(seconds) = retry_after {
60 write!(f, "Rate limited, retry after {} seconds", seconds)
61 } else {
62 write!(f, "Rate limited by Cloudflare API")
63 }
64 }
65 }
66 }
67}
68
69impl std::error::Error for Error {
70 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
71 match self {
72 Error::Http(e) => Some(e),
73 Error::Json(e) => Some(e),
74 _ => None,
75 }
76 }
77}
78
79impl From<reqwest::Error> for Error {
80 fn from(err: reqwest::Error) -> Self {
81 Error::Http(err)
82 }
83}
84
85impl From<serde_json::Error> for Error {
86 fn from(err: serde_json::Error) -> Self {
87 Error::Json(err)
88 }
89}
90
91impl ApiError {
92 pub fn new(status: u16, message: String, body: String) -> Self {
94 Self {
95 status,
96 code: None,
97 message,
98 body,
99 }
100 }
101
102 pub(crate) fn from_response(status: u16, body: &str) -> Self {
104 if let Ok(json) = serde_json::from_str::<serde_json::Value>(body)
106 && let Some(errors) = json["errors"].as_array()
107 && let Some(first_error) = errors.first()
108 {
109 return Self {
110 status,
111 code: first_error["code"].as_i64().map(|c| c as i32),
112 message: first_error["message"]
113 .as_str()
114 .unwrap_or("Unknown error")
115 .to_string(),
116 body: body.to_string(),
117 };
118 }
119
120 Self {
121 status,
122 code: None,
123 message: format!("HTTP {}", status),
124 body: body.to_string(),
125 }
126 }
127}