1use thiserror::Error;
2
3#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
4pub struct RateLimitPolicyEntry {
5 pub name: String,
6 pub quota: Option<i64>,
7 pub unit: Option<String>,
8 pub window_seconds: Option<u64>,
9}
10
11#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
12pub struct RateLimitStateEntry {
13 pub name: String,
14 pub remaining: Option<i64>,
15 pub unit: Option<String>,
16 pub reset_after_seconds: Option<u64>,
17}
18
19#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
20pub struct ResponseMeta {
21 pub request_id: Option<String>,
22 pub retry_after_raw: Option<String>,
23 pub retry_after_seconds: Option<u64>,
24 pub debit_status: Option<String>,
25 pub credits_requested: Option<i64>,
26 pub credits_charged: Option<i64>,
27 pub credits_pricing: Option<String>,
28 pub active_quota_buckets: Option<u64>,
29 pub stop_on_empty: Option<bool>,
30 pub rate_limit_policy_raw: Option<String>,
31 pub rate_limit_raw: Option<String>,
32 pub rate_limit_policies: std::collections::BTreeMap<String, RateLimitPolicyEntry>,
33 pub rate_limits: std::collections::BTreeMap<String, RateLimitStateEntry>,
34 pub balance_limit_cents: Option<i64>,
35 pub balance_remaining_cents: Option<i64>,
36 pub quota_limit_credits: Option<i64>,
37 pub quota_remaining_credits: Option<i64>,
38 pub visitor_quota_limit_credits: Option<i64>,
39 pub visitor_quota_remaining_credits: Option<i64>,
40 pub billing_key_rate_limit: Option<i64>,
41 pub billing_key_rate_remaining: Option<i64>,
42 pub billing_key_rate_unit: Option<String>,
43 pub billing_key_rate_window_seconds: Option<u64>,
44 pub billing_key_rate_reset_after_seconds: Option<u64>,
45 pub billing_ip_rate_limit: Option<i64>,
46 pub billing_ip_rate_remaining: Option<i64>,
47 pub billing_ip_rate_unit: Option<String>,
48 pub billing_ip_rate_window_seconds: Option<u64>,
49 pub billing_ip_rate_reset_after_seconds: Option<u64>,
50 pub visitor_rate_limit: Option<i64>,
51 pub visitor_rate_remaining: Option<i64>,
52 pub visitor_rate_unit: Option<String>,
53 pub visitor_rate_window_seconds: Option<u64>,
54 pub visitor_rate_reset_after_seconds: Option<u64>,
55 pub raw_headers: std::collections::BTreeMap<String, String>,
56}
57
58#[derive(Debug, Error)]
59pub enum Error {
60 #[error("authentication failed (status {status})")]
61 AuthenticationError {
62 status: u16,
63 message: Option<String>,
64 request_id: Option<String>,
65 meta: Option<ResponseMeta>,
66 },
67
68 #[error("rate limited (status {status})")]
69 RateLimitError {
70 status: u16,
71 message: Option<String>,
72 retry_after_seconds: Option<u64>,
73 request_id: Option<String>,
74 meta: Option<ResponseMeta>,
75 },
76
77 #[error("insufficient credits (status {status})")]
78 InsufficientCredits {
79 status: u16,
80 message: Option<String>,
81 details: Option<serde_json::Value>,
82 request_id: Option<String>,
83 meta: Option<ResponseMeta>,
84 },
85
86 #[error("visitor monthly quota exhausted (status {status})")]
87 VisitorMonthlyQuotaExhausted {
88 status: u16,
89 message: Option<String>,
90 details: Option<serde_json::Value>,
91 request_id: Option<String>,
92 meta: Option<ResponseMeta>,
93 },
94
95 #[error("validation failed (status {status})")]
96 ValidationError {
97 status: u16,
98 message: Option<String>,
99 details: Option<serde_json::Value>,
100 request_id: Option<String>,
101 meta: Option<ResponseMeta>,
102 },
103
104 #[error("resource not found (status {status})")]
105 NotFound {
106 status: u16,
107 message: Option<String>,
108 request_id: Option<String>,
109 meta: Option<ResponseMeta>,
110 },
111
112 #[error("server error (status {status})")]
113 ServerError {
114 status: u16,
115 message: Option<String>,
116 request_id: Option<String>,
117 meta: Option<ResponseMeta>,
118 },
119
120 #[error("api error (status {status})")]
121 ApiError {
122 status: u16,
123 code: Option<String>,
124 message: Option<String>,
125 details: Option<serde_json::Value>,
126 request_id: Option<String>,
127 meta: Option<ResponseMeta>,
128 },
129
130 #[error("invalid header `{name}`")]
131 InvalidHeader { name: String },
132
133 #[error(transparent)]
134 Transport(#[from] reqwest::Error),
135
136 #[error(transparent)]
137 Json(#[from] serde_json::Error),
138
139 #[error(transparent)]
140 Url(#[from] url::ParseError),
141}
142
143impl Error {
144 pub fn status(&self) -> Option<u16> {
145 use Error::*;
146 match self {
147 AuthenticationError { status, .. }
148 | RateLimitError { status, .. }
149 | InsufficientCredits { status, .. }
150 | VisitorMonthlyQuotaExhausted { status, .. }
151 | ValidationError { status, .. }
152 | NotFound { status, .. }
153 | ServerError { status, .. }
154 | ApiError { status, .. } => Some(*status),
155 _ => None,
156 }
157 }
158
159 pub fn request_id(&self) -> Option<&str> {
160 use Error::*;
161 match self {
162 AuthenticationError { request_id, .. }
163 | RateLimitError { request_id, .. }
164 | InsufficientCredits { request_id, .. }
165 | VisitorMonthlyQuotaExhausted { request_id, .. }
166 | ValidationError { request_id, .. }
167 | NotFound { request_id, .. }
168 | ServerError { request_id, .. }
169 | ApiError { request_id, .. } => request_id.as_deref(),
170 _ => None,
171 }
172 }
173
174 pub fn meta(&self) -> Option<&ResponseMeta> {
175 use Error::*;
176 match self {
177 AuthenticationError { meta, .. }
178 | RateLimitError { meta, .. }
179 | InsufficientCredits { meta, .. }
180 | VisitorMonthlyQuotaExhausted { meta, .. }
181 | ValidationError { meta, .. }
182 | NotFound { meta, .. }
183 | ServerError { meta, .. }
184 | ApiError { meta, .. } => meta.as_ref(),
185 _ => None,
186 }
187 }
188}
189
190#[derive(Debug, serde::Deserialize)]
191pub struct ApiErrorBody {
192 pub code: Option<String>,
193 pub error: Option<String>,
194 pub message: Option<String>,
195 pub details: Option<serde_json::Value>,
196 pub quota: Option<serde_json::Value>,
197 pub docs: Option<serde_json::Value>,
198}