xmtp_proto/traits/
error.rs1use std::fmt::Display;
2
3use crate::{ApiEndpoint, ProtoError};
4use thiserror::Error;
5use xmtp_common::{BoxDynError, RetryableError, retryable};
6
7#[derive(Debug, Error)]
8#[non_exhaustive]
9pub enum ApiClientError {
10 #[error("api client at endpoint \"{}\" has error {}", endpoint, source)]
12 ClientWithEndpoint {
13 endpoint: String,
14 source: NetworkError,
16 },
17 #[error("client errored {}", source)]
18 Client { source: NetworkError },
19 #[error(transparent)]
20 Http(#[from] http::Error),
21 #[error(transparent)]
22 Body(#[from] BodyError),
23 #[error(transparent)]
24 DecodeError(#[from] prost::DecodeError),
25 #[error(transparent)]
26 Conversion(#[from] crate::ConversionError),
27 #[error(transparent)]
28 ProtoError(#[from] ProtoError),
29 #[error(transparent)]
30 InvalidUri(#[from] http::uri::InvalidUri),
31 #[error(transparent)]
32 Expired(#[from] xmtp_common::time::Expired),
33 #[error("{0}")]
34 Other(Box<dyn RetryableError>),
35 #[error("{0}")]
36 OtherUnretryable(BoxDynError),
37 #[error("Writes are disabled on this client.")]
38 WritesDisabled,
39}
40
41#[derive(Debug)]
45pub struct NetworkError {
46 source: Box<dyn RetryableError>,
47}
48
49impl std::error::Error for NetworkError {
50 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
51 Some(self.source.as_ref())
52 }
53}
54
55impl Display for NetworkError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "{}", self.source)
58 }
59}
60
61impl RetryableError for NetworkError {
62 fn is_retryable(&self) -> bool {
63 self.source.is_retryable()
64 }
65}
66
67impl NetworkError {
68 pub fn new(e: impl RetryableError + 'static) -> Self {
69 NetworkError {
70 source: Box::new(e),
71 }
72 }
73}
74
75impl ApiClientError {
76 pub fn new(endpoint: ApiEndpoint, source: impl RetryableError + 'static) -> Self {
77 Self::ClientWithEndpoint {
78 endpoint: endpoint.to_string(),
79 source: NetworkError::new(source),
80 }
81 }
82
83 pub fn endpoint(self, endpoint: impl ToString) -> Self {
85 match self {
86 Self::Client { source } => Self::ClientWithEndpoint {
87 source,
88 endpoint: endpoint.to_string(),
89 },
90 v => v,
91 }
92 }
93
94 pub fn client(client: impl RetryableError + 'static) -> Self {
95 Self::Client {
96 source: NetworkError::new(client),
97 }
98 }
99
100 pub fn network_error(&self) -> Option<&NetworkError> {
103 use ApiClientError::*;
104 match self {
105 ClientWithEndpoint { source, .. } | Client { source, .. } => Some(source),
106 _ => None,
107 }
108 }
109}
110
111impl ApiClientError {
112 pub fn other<R: RetryableError + 'static>(e: R) -> Self {
113 ApiClientError::Other(Box::new(e))
114 }
115}
116
117impl RetryableError for ApiClientError {
118 fn is_retryable(&self) -> bool {
119 use ApiClientError::*;
120 match self {
121 Client { source } => retryable!(*source),
122 ClientWithEndpoint { source, .. } => retryable!(source),
123 Body(e) => retryable!(e),
124 Http(_) => true,
125 DecodeError(_) => false,
126 Conversion(_) => false,
127 ProtoError(_) => false,
128 InvalidUri(_) => false,
129 Expired(_) => true,
130 Other(r) => retryable!(r),
131 OtherUnretryable(_) => false,
132 WritesDisabled => false,
133 }
134 }
135}
136
137impl From<std::convert::Infallible> for ApiClientError {
139 fn from(_v: std::convert::Infallible) -> ApiClientError {
140 unreachable!("Infallible errors can never occur")
141 }
142}
143
144#[derive(Debug, Error)]
145pub enum BodyError {
146 #[error(transparent)]
147 UninitializedField(#[from] derive_builder::UninitializedFieldError),
148 #[error(transparent)]
149 Conversion(#[from] crate::ConversionError),
150}
151
152impl RetryableError for BodyError {
153 fn is_retryable(&self) -> bool {
154 false
155 }
156}