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(transparent)]
97 Transport(#[from] reqwest::Error),
98
99 #[error(transparent)]
100 Json(#[from] serde_json::Error),
101
102 #[error(transparent)]
103 Url(#[from] url::ParseError),
104}
105
106impl Error {
107 pub fn status(&self) -> Option<u16> {
108 use Error::*;
109 match self {
110 AuthenticationError { status, .. }
111 | RateLimitError { status, .. }
112 | InsufficientCredits { status, .. }
113 | VisitorMonthlyQuotaExhausted { status, .. }
114 | ValidationError { status, .. }
115 | NotFound { status, .. }
116 | ServerError { status, .. }
117 | ApiError { status, .. } => Some(*status),
118 _ => None,
119 }
120 }
121
122 pub fn request_id(&self) -> Option<&str> {
123 use Error::*;
124 match self {
125 AuthenticationError { request_id, .. }
126 | RateLimitError { request_id, .. }
127 | InsufficientCredits { request_id, .. }
128 | VisitorMonthlyQuotaExhausted { request_id, .. }
129 | ValidationError { request_id, .. }
130 | NotFound { request_id, .. }
131 | ServerError { request_id, .. }
132 | ApiError { request_id, .. } => request_id.as_deref(),
133 _ => None,
134 }
135 }
136
137 pub fn meta(&self) -> Option<&ResponseMeta> {
138 use Error::*;
139 match self {
140 AuthenticationError { meta, .. }
141 | RateLimitError { meta, .. }
142 | InsufficientCredits { meta, .. }
143 | VisitorMonthlyQuotaExhausted { meta, .. }
144 | ValidationError { meta, .. }
145 | NotFound { meta, .. }
146 | ServerError { meta, .. }
147 | ApiError { meta, .. } => meta.as_ref(),
148 _ => None,
149 }
150 }
151}
152
153#[derive(Debug, serde::Deserialize)]
154pub struct ApiErrorBody {
155 pub code: Option<String>,
156 pub error: Option<String>,
157 pub message: Option<String>,
158 pub details: Option<serde_json::Value>,
159 pub quota: Option<serde_json::Value>,
160 pub docs: Option<serde_json::Value>,
161}