Skip to main content

euromail/
errors.rs

1use serde::Deserialize;
2
3/// Errors returned by the EuroMail SDK.
4///
5/// All API methods return `Result<T, EuroMailError>`. HTTP-level errors from the
6/// EuroMail API are mapped to specific variants based on status code, while
7/// network and deserialization failures surface as [`EuroMailError::Http`].
8///
9/// # Example
10///
11/// ```rust,no_run
12/// # use euromail::{EuroMail, EuroMailError};
13/// # async fn run() -> Result<(), EuroMailError> {
14/// let client = EuroMail::new("em_live_key");
15/// match client.get_email("nonexistent").await {
16///     Err(EuroMailError::NotFound(msg)) => eprintln!("Not found: {msg}"),
17///     Err(EuroMailError::RateLimit { retry_after, .. }) => {
18///         eprintln!("Rate limited, retry after {retry_after:?}s");
19///     }
20///     Err(e) => eprintln!("Error: {e}"),
21///     Ok(detail) => println!("Email: {}", detail.email.id),
22/// }
23/// # Ok(())
24/// # }
25/// ```
26#[derive(Debug, thiserror::Error)]
27pub enum EuroMailError {
28    /// Invalid or expired API key (HTTP 401).
29    #[error("Authentication failed: {0}")]
30    Authentication(String),
31
32    /// Request failed validation — e.g. missing required fields (HTTP 422).
33    #[error("Validation error [{code}]: {message}")]
34    Validation { code: String, message: String },
35
36    /// Too many requests. `retry_after` contains the suggested wait in seconds
37    /// if the server provided a `Retry-After` header (HTTP 429).
38    #[error("Rate limit exceeded: {message}")]
39    RateLimit {
40        retry_after: Option<u64>,
41        message: String,
42    },
43
44    /// The requested resource does not exist (HTTP 404).
45    #[error("Not found: {0}")]
46    NotFound(String),
47
48    /// Any other API error (HTTP 4xx/5xx).
49    #[error("API error [{status}] {code}: {message}")]
50    Api {
51        status: u16,
52        code: String,
53        message: String,
54    },
55
56    /// Network or deserialization error from the underlying HTTP client.
57    #[error("HTTP error: {0}")]
58    Http(#[from] reqwest::Error),
59}
60
61#[derive(Deserialize)]
62pub(crate) struct ApiErrorBody {
63    #[serde(default = "default_code")]
64    pub code: String,
65    #[serde(default = "default_message")]
66    pub message: String,
67}
68
69fn default_code() -> String {
70    "unknown".to_string()
71}
72
73fn default_message() -> String {
74    "Unknown error".to_string()
75}