xmtp_proto/traits/
error.rs

1use crate::api_client::AggregateStats;
2use crate::{ApiEndpoint, ProtoError};
3use thiserror::Error;
4use xmtp_common::{BoxDynError, RetryableError, retryable};
5
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum ApiClientError<E: std::error::Error> {
9    #[error(
10        "api client at endpoint \"{}\" has error {}. \n {:?} \n",
11        endpoint,
12        source,
13        stats
14    )]
15    ClientWithEndpointAndStats {
16        endpoint: String,
17        source: E,
18        stats: AggregateStats,
19    },
20    #[error("API Error {}, \n {:?} \n", e, stats)]
21    ErrorWithStats {
22        e: Box<dyn RetryableError>,
23        stats: AggregateStats,
24    },
25    /// The client encountered an error.
26    #[error("api client at endpoint \"{}\" has error {}", endpoint, source)]
27    ClientWithEndpoint {
28        endpoint: String,
29        /// The client error.
30        source: E,
31    },
32    #[error("client errored {}", source)]
33    Client { source: E },
34    #[error(transparent)]
35    Http(#[from] http::Error),
36    #[error(transparent)]
37    Body(#[from] BodyError),
38    #[error(transparent)]
39    DecodeError(#[from] prost::DecodeError),
40    #[error(transparent)]
41    Conversion(#[from] crate::ConversionError),
42    #[error(transparent)]
43    ProtoError(#[from] ProtoError),
44    #[error(transparent)]
45    InvalidUri(#[from] http::uri::InvalidUri),
46    #[error(transparent)]
47    Expired(#[from] xmtp_common::time::Expired),
48    #[error("{0}")]
49    Other(Box<dyn RetryableError>),
50    #[error("{0}")]
51    OtherUnretryable(BoxDynError),
52    #[error("Writes are disabled on this client.")]
53    WritesDisabled,
54}
55
56impl<E> ApiClientError<E>
57where
58    E: std::error::Error + 'static,
59{
60    pub fn new(endpoint: ApiEndpoint, source: E) -> Self {
61        Self::ClientWithEndpoint {
62            endpoint: endpoint.to_string(),
63            source,
64        }
65    }
66
67    /// add an endpoint to a ApiError::Client error
68    pub fn endpoint(self, endpoint: String) -> Self {
69        match self {
70            Self::Client { source } => Self::ClientWithEndpoint { source, endpoint },
71            v => v,
72        }
73    }
74}
75
76impl<E: std::error::Error> ApiClientError<E> {
77    pub fn other<R: RetryableError + 'static>(e: R) -> Self {
78        ApiClientError::Other(Box::new(e))
79    }
80}
81
82impl<E> RetryableError for ApiClientError<E>
83where
84    E: RetryableError + std::error::Error + 'static,
85{
86    fn is_retryable(&self) -> bool {
87        use ApiClientError::*;
88        match self {
89            ClientWithEndpointAndStats { source, .. } => retryable!(source),
90            ErrorWithStats { e, .. } => retryable!(e),
91            Client { source } => retryable!(*source),
92            ClientWithEndpoint { source, .. } => retryable!(source),
93            Body(e) => retryable!(e),
94            Http(_) => true,
95            DecodeError(_) => false,
96            Conversion(_) => false,
97            ProtoError(_) => false,
98            InvalidUri(_) => false,
99            Expired(_) => true,
100            Other(r) => retryable!(r),
101            OtherUnretryable(_) => false,
102            WritesDisabled => false,
103        }
104    }
105}
106
107// Infallible errors by definition can never occur
108impl<E: std::error::Error> From<std::convert::Infallible> for ApiClientError<E> {
109    fn from(_v: std::convert::Infallible) -> ApiClientError<E> {
110        unreachable!("Infallible errors can never occur")
111    }
112}
113
114#[derive(Debug, Error)]
115pub enum BodyError {
116    #[error(transparent)]
117    UninitializedField(#[from] derive_builder::UninitializedFieldError),
118    #[error(transparent)]
119    Conversion(#[from] crate::ConversionError),
120}
121
122impl RetryableError for BodyError {
123    fn is_retryable(&self) -> bool {
124        false
125    }
126}