blob_indexer/clients/
common.rs

1use std::{fmt::Display, str::FromStr};
2
3use serde::Deserialize;
4
5#[derive(Clone, Debug, Deserialize)]
6#[serde(untagged)]
7pub enum NumericOrTextCode {
8    String(String),
9    Number(usize),
10}
11/// API Error response
12#[derive(Deserialize, Debug, Clone)]
13pub struct ErrorResponse {
14    /// Error code
15    pub code: NumericOrTextCode,
16    /// Error message
17    #[serde(default)]
18    pub message: Option<String>,
19}
20
21#[derive(Debug, thiserror::Error)]
22pub enum ClientError {
23    /// Reqwest Error
24    #[error(transparent)]
25    Reqwest(#[from] reqwest::Error),
26
27    /// API Error
28    #[error("API usage error: {0}")]
29    ApiError(ErrorResponse),
30
31    /// Other Error
32    #[error(transparent)]
33    Other(#[from] anyhow::Error),
34
35    /// Url Parsing Error
36    #[error("{0}")]
37    UrlParse(#[from] url::ParseError),
38
39    /// Serde Json deser Error
40    #[error("{0}")]
41    SerdeError(#[from] serde_json::Error),
42}
43
44/// API Response
45#[derive(Deserialize, Debug, Clone)]
46#[serde(untagged)]
47pub enum ClientResponse<T> {
48    /// Error
49    Error(ErrorResponse),
50    /// Success w/ value
51    Success(T),
52    /// Empty Success
53    EmptySuccess,
54}
55
56pub type ClientResult<T> = Result<T, ClientError>;
57
58impl<T> ClientResponse<T> {
59    pub(crate) fn into_client_result(self) -> ClientResult<Option<T>> {
60        match self {
61            ClientResponse::Error(e) => Err(e.into()),
62            ClientResponse::Success(t) => Ok(Some(t)),
63            ClientResponse::EmptySuccess => Ok(None),
64        }
65    }
66
67    /// True if the response is an API error
68    pub fn is_err(&self) -> bool {
69        matches!(self, Self::Error(_))
70    }
71}
72
73impl<T> FromStr for ClientResponse<T>
74where
75    T: serde::de::DeserializeOwned,
76{
77    type Err = serde_json::Error;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        if s.is_empty() {
81            return Ok(ClientResponse::EmptySuccess);
82        }
83        serde_json::from_str(s)
84    }
85}
86
87impl From<ErrorResponse> for ClientError {
88    fn from(err: ErrorResponse) -> Self {
89        Self::ApiError(err)
90    }
91}
92
93impl Display for NumericOrTextCode {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        match self {
96            Self::String(s) => f.write_str(s.to_string().as_ref()),
97            Self::Number(n) => f.write_str(n.to_string().as_ref()),
98        }
99    }
100}
101impl Display for ErrorResponse {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        f.write_str(&format!(
104            "Code: {}, Message: \"{}\"",
105            self.code,
106            self.message.as_deref().unwrap_or(""),
107        ))
108    }
109}