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_seconds: Option<u64>,
23 pub debit_status: Option<String>,
24 pub credits_requested: Option<i64>,
25 pub credits_charged: Option<i64>,
26 pub credits_pricing: Option<String>,
27 pub active_quota_buckets: Option<u64>,
28 pub stop_on_empty: Option<bool>,
29 pub rate_limit_policy_raw: Option<String>,
30 pub rate_limit_raw: Option<String>,
31 pub rate_limit_policies: std::collections::BTreeMap<String, RateLimitPolicyEntry>,
32 pub rate_limits: std::collections::BTreeMap<String, RateLimitStateEntry>,
33 pub balance_limit_cents: Option<i64>,
34 pub balance_remaining_cents: Option<i64>,
35 pub quota_limit_credits: Option<i64>,
36 pub quota_remaining_credits: Option<i64>,
37 pub visitor_quota_limit_credits: Option<i64>,
38 pub visitor_quota_remaining_credits: Option<i64>,
39 pub raw_headers: std::collections::BTreeMap<String, String>,
40}
41
42#[derive(Debug, Error)]
43pub enum Error {
44 #[error("authentication failed (status {status})")]
45 AuthenticationError { status: u16, message: Option<String>, request_id: Option<String>, meta: Option<ResponseMeta> },
46
47 #[error("rate limited (status {status})")]
48 RateLimitError {
49 status: u16,
50 message: Option<String>,
51 retry_after_seconds: Option<u64>,
52 request_id: Option<String>,
53 meta: Option<ResponseMeta>,
54 },
55
56 #[error("insufficient credits (status {status})")]
57 InsufficientCredits {
58 status: u16,
59 message: Option<String>,
60 details: Option<serde_json::Value>,
61 request_id: Option<String>,
62 meta: Option<ResponseMeta>,
63 },
64
65 #[error("visitor monthly quota exhausted (status {status})")]
66 VisitorMonthlyQuotaExhausted {
67 status: u16,
68 message: Option<String>,
69 details: Option<serde_json::Value>,
70 request_id: Option<String>,
71 meta: Option<ResponseMeta>,
72 },
73
74 #[error("validation failed (status {status})")]
75 ValidationError { status: u16, message: Option<String>, details: Option<serde_json::Value>, request_id: Option<String>, meta: Option<ResponseMeta> },
76
77 #[error("resource not found (status {status})")]
78 NotFound { status: u16, message: Option<String>, request_id: Option<String>, meta: Option<ResponseMeta> },
79
80 #[error("server error (status {status})")]
81 ServerError { status: u16, message: Option<String>, request_id: Option<String>, meta: Option<ResponseMeta> },
82
83 #[error("api error (status {status})")]
84 ApiError {
85 status: u16,
86 code: Option<String>,
87 message: Option<String>,
88 details: Option<serde_json::Value>,
89 request_id: Option<String>,
90 meta: Option<ResponseMeta>,
91 },
92
93 #[error("invalid header `{name}`")]
94 InvalidHeader { name: String },
95
96 #[error("invalid multipart body: {message}")]
97 InvalidMultipartBody { message: String },
98
99 #[error(transparent)]
100 Io(#[from] std::io::Error),
101
102 #[error(transparent)]
103 Transport(#[from] reqwest::Error),
104
105 #[error(transparent)]
106 Json(#[from] serde_json::Error),
107
108 #[error(transparent)]
109 Url(#[from] url::ParseError),
110}
111
112impl Error {
113 pub fn status(&self) -> Option<u16> {
114 use Error::*;
115 match self {
116 AuthenticationError { status, .. }
117 | RateLimitError { status, .. }
118 | InsufficientCredits { status, .. }
119 | VisitorMonthlyQuotaExhausted { status, .. }
120 | ValidationError { status, .. }
121 | NotFound { status, .. }
122 | ServerError { status, .. }
123 | ApiError { status, .. } => Some(*status),
124 _ => None,
125 }
126 }
127
128 pub fn request_id(&self) -> Option<&str> {
129 use Error::*;
130 match self {
131 AuthenticationError { request_id, .. }
132 | RateLimitError { request_id, .. }
133 | InsufficientCredits { request_id, .. }
134 | VisitorMonthlyQuotaExhausted { request_id, .. }
135 | ValidationError { request_id, .. }
136 | NotFound { request_id, .. }
137 | ServerError { request_id, .. }
138 | ApiError { request_id, .. } => request_id.as_deref(),
139 _ => None,
140 }
141 }
142
143 pub fn meta(&self) -> Option<&ResponseMeta> {
144 use Error::*;
145 match self {
146 AuthenticationError { meta, .. }
147 | RateLimitError { meta, .. }
148 | InsufficientCredits { meta, .. }
149 | VisitorMonthlyQuotaExhausted { meta, .. }
150 | ValidationError { meta, .. }
151 | NotFound { meta, .. }
152 | ServerError { meta, .. }
153 | ApiError { meta, .. } => meta.as_ref(),
154 _ => None,
155 }
156 }
157}
158
159#[derive(Debug, serde::Deserialize)]
160pub struct ApiErrorBody {
161 pub code: Option<String>,
162 pub error: Option<String>,
163 pub message: Option<String>,
164 pub details: Option<serde_json::Value>,
165 pub quota: Option<serde_json::Value>,
166 pub docs: Option<serde_json::Value>,
167}