Skip to main content

data_for_seo/
error.rs

1//! Error types returned by the client.
2
3use serde_json::Value;
4use std::fmt;
5
6/// Any error produced by a DataForSEO API call.
7#[derive(Debug, Clone)]
8pub enum DataForSeoError {
9    /// The API answered with an error envelope (HTTP 4xx/5xx and a
10    /// DataForSEO `status_code` / `status_message`).
11    Api(DataForSeoApiError),
12    /// The server answered with a non-JSON HTTP failure.
13    Http(DataForSeoHttpError),
14    /// Transport or (de)serialization failure.
15    System(DataForSeoSystemError),
16}
17
18impl DataForSeoError {
19    /// HTTP status of the failed call, or `0` for transport-level failures.
20    pub fn http_status(&self) -> u16 {
21        match self {
22            DataForSeoError::Api(e) => e.http_status,
23            DataForSeoError::Http(e) => e.http_status,
24            DataForSeoError::System(_) => 0,
25        }
26    }
27
28    /// DataForSEO status code (e.g. `40101`) when the API returned one.
29    /// See <https://docs.dataforseo.com/v3/appendix/errors/>.
30    pub fn api_status_code(&self) -> Option<i32> {
31        match self {
32            DataForSeoError::Api(e) => Some(e.status_code),
33            _ => None,
34        }
35    }
36}
37
38impl fmt::Display for DataForSeoError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            DataForSeoError::Api(e) => write!(
42                f,
43                "DataForSEO API error (http {}): {} {}",
44                e.http_status, e.status_code, e.status_message
45            ),
46            DataForSeoError::Http(e) => write!(f, "HTTP error {}", e.http_status),
47            DataForSeoError::System(e) => write!(f, "client error: {}", e.message),
48        }
49    }
50}
51
52impl std::error::Error for DataForSeoError {}
53
54impl From<DataForSeoApiError> for DataForSeoError {
55    fn from(value: DataForSeoApiError) -> Self {
56        DataForSeoError::Api(value)
57    }
58}
59
60impl From<DataForSeoHttpError> for DataForSeoError {
61    fn from(value: DataForSeoHttpError) -> Self {
62        DataForSeoError::Http(value)
63    }
64}
65
66impl From<DataForSeoSystemError> for DataForSeoError {
67    fn from(value: DataForSeoSystemError) -> Self {
68        DataForSeoError::System(value)
69    }
70}
71
72/// Error envelope returned by the DataForSEO API.
73///
74/// ```json
75/// {"version":"0.1.x","status_code":40101,"status_message":"Authentication failed. ..."}
76/// ```
77#[derive(Debug, Clone)]
78pub struct DataForSeoApiError {
79    /// HTTP status of the response.
80    pub http_status: u16,
81    /// DataForSEO status code, e.g. `40101`.
82    /// See <https://docs.dataforseo.com/v3/appendix/errors/>.
83    pub status_code: i32,
84    /// Human-readable message from the API.
85    pub status_message: String,
86    /// Raw response body, for debugging.
87    pub response_body: String,
88}
89
90impl DataForSeoApiError {
91    pub(crate) fn from_envelope(http_status: u16, value: &Value, body: String) -> Self {
92        DataForSeoApiError {
93            http_status,
94            status_code: value
95                .get("status_code")
96                .and_then(Value::as_i64)
97                .unwrap_or_default() as i32,
98            status_message: value
99                .get("status_message")
100                .and_then(Value::as_str)
101                .unwrap_or_default()
102                .to_string(),
103            response_body: body,
104        }
105    }
106}
107
108/// Non-JSON HTTP failure (e.g. a gateway error page).
109#[derive(Debug, Clone)]
110pub struct DataForSeoHttpError {
111    /// HTTP status of the response.
112    pub http_status: u16,
113    /// Raw response body, for debugging.
114    pub response_body: String,
115}
116
117impl DataForSeoHttpError {
118    /// Creates an HTTP error from a status code and raw body.
119    pub fn new(http_status: u16, response_body: String) -> DataForSeoHttpError {
120        DataForSeoHttpError {
121            http_status,
122            response_body,
123        }
124    }
125}
126
127/// Transport or (de)serialization failure.
128#[derive(Debug, Clone)]
129pub struct DataForSeoSystemError {
130    /// Description of what failed.
131    pub message: String,
132}
133
134impl DataForSeoSystemError {
135    /// Creates a system error from a message.
136    pub fn new<T: Into<String>>(message: T) -> DataForSeoSystemError {
137        DataForSeoSystemError {
138            message: message.into(),
139        }
140    }
141}