scrapebadger/core/error.rs
1use serde_json::Value;
2
3/// Convenient result alias used throughout the crate.
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// Every error the SDK can produce.
7///
8/// API failures are mapped to typed variants by HTTP status where the meaning
9/// is well-defined (`401`, `402`, `422`, `429`); anything else lands in
10/// [`Error::Api`] with the raw status and decoded JSON body so callers never
11/// lose information.
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum Error {
15 /// No API key was supplied and `SCRAPEBADGER_API_KEY` was not set.
16 #[error("missing API key: pass one to ScrapeBadger::new or set SCRAPEBADGER_API_KEY")]
17 MissingApiKey,
18
19 /// The configured base URL could not be used to build a request URL.
20 #[error("invalid URL: {0}")]
21 InvalidUrl(String),
22
23 /// Transport-level failure (DNS, TLS, connection, timeout).
24 #[error("http transport error: {0}")]
25 Transport(#[from] reqwest::Error),
26
27 /// `401 Unauthorized` — the API key is missing or invalid.
28 #[error("unauthorized (401): {message}")]
29 Unauthorized { message: String },
30
31 /// `402 Payment Required` — out of credits or no active subscription.
32 #[error("payment required (402): {message}")]
33 PaymentRequired { message: String },
34
35 /// `429 Too Many Requests` — rate limit exceeded.
36 #[error("rate limited (429){}", match retry_after { Some(s) => format!(", retry after {s}s"), None => String::new() })]
37 RateLimited {
38 /// Seconds to wait before retrying, if the server told us.
39 retry_after: Option<u64>,
40 message: String,
41 },
42
43 /// `422 Unprocessable Entity` — request validation failed.
44 #[error("validation error (422): {message}")]
45 Validation {
46 message: String,
47 /// The raw FastAPI `detail` array.
48 detail: Value,
49 },
50
51 /// Any other non-success HTTP status.
52 #[error("api error (status {status}): {message}")]
53 Api {
54 status: u16,
55 message: String,
56 /// Decoded JSON body (or `Value::Null` if the body was not JSON).
57 body: Value,
58 },
59
60 /// The response body could not be decoded into the expected type.
61 #[error("failed to decode response: {0}")]
62 Decode(String),
63
64 /// WebSocket streaming error (Twitter Streams).
65 #[cfg(feature = "stream")]
66 #[error("websocket error: {0}")]
67 WebSocket(String),
68}