Skip to main content

rpc/
error.rs

1use reqwest::StatusCode;
2use thiserror::Error;
3use url::ParseError;
4
5#[derive(Debug, Error)]
6pub enum ApiClientError {
7    #[error("invalid base URL: {0}")]
8    InvalidUrl(#[from] ParseError),
9
10    #[error("transport error: {0}")]
11    Transport(#[from] reqwest::Error),
12
13    #[error("failed to decode response body: {0}")]
14    Decode(#[from] serde_json::Error),
15
16    #[error("server returned {status}: {message}")]
17    Api { status: StatusCode, message: String },
18}
19
20impl ApiClientError {
21    pub fn status(&self) -> Option<StatusCode> {
22        match self {
23            Self::Api { status, .. } => Some(*status),
24            _ => None,
25        }
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn status_returns_some_for_api_errors() {
35        let err = ApiClientError::Api {
36            status: StatusCode::BAD_REQUEST,
37            message: "oops".into(),
38        };
39        assert_eq!(err.status(), Some(StatusCode::BAD_REQUEST));
40    }
41
42    #[test]
43    fn status_returns_none_for_other_variants() {
44        let decode_err: ApiClientError = serde_json::from_str::<serde_json::Value>("not-json")
45            .unwrap_err()
46            .into();
47        assert!(matches!(decode_err, ApiClientError::Decode(_)));
48        assert_eq!(decode_err.status(), None);
49    }
50}