1use core::time::Duration;
4
5use flex_error::{define_error, DefaultTracer, DisplayError, DisplayOnly, ErrorMessageTracer};
6
7use crate::{prelude::*, response_error::ResponseError, rpc_url::Url};
8
9#[cfg(feature = "reqwest")]
10type ReqwestError = flex_error::DisplayOnly<reqwest::Error>;
11
12#[cfg(not(feature = "reqwest"))]
13type ReqwestError = flex_error::NoSource;
14
15#[cfg(feature = "reqwest")]
16type HttpStatusCode = reqwest::StatusCode;
17
18#[cfg(not(feature = "reqwest"))]
19type HttpStatusCode = core::num::NonZeroU16;
20
21#[cfg(feature = "tokio")]
22type JoinError = flex_error::DisplayOnly<tokio::task::JoinError>;
23
24#[cfg(not(feature = "tokio"))]
25type JoinError = flex_error::NoSource;
26
27#[cfg(feature = "async-tungstenite")]
28type TungsteniteError = flex_error::DisplayOnly<async_tungstenite::tungstenite::Error>;
29
30#[cfg(not(feature = "async-tungstenite"))]
31type TungsteniteError = flex_error::NoSource;
32
33define_error! {
34    #[derive(Debug, Clone, PartialEq, Eq)]
35    Error {
36        Response
37            [ DisplayError<ResponseError> ]
38            | _ | { "response error" },
39
40        Io
41            [ DisplayOnly<std::io::Error> ]
42            | _ | { "I/O error" },
43
44        Http
45            [ ReqwestError ]
46            | _ | { "HTTP error" },
47
48        InvalidProxy
49            [ ReqwestError ]
50            | _ | { "Invalid proxy configuration" },
51
52        InvalidParams
53            {
54                message: String
55            }
56            | e | {
57                format_args!("invalid params error: {}", e.message)
58            },
59
60        WebSocket
61            {
62                message: String
63            }
64            [ TungsteniteError ]
65            | e | {
66                format_args!("web socket error: {}", e.message)
67            },
68
69        WebSocketTimeout
70            {
71                timeout: Duration
72            }
73            | e | {
74                format_args!("reading from WebSocket connection timed out after {} seconds",
75                    e.timeout.as_secs())
76            },
77
78        MethodNotFound
79            {
80                method: String
81            }
82            | e | {
83                format_args!("method not found: {}", e.method)
84            },
85
86        HttpRequestFailed
87            {
88                status: HttpStatusCode,
89            }
90            | e | {
91                format_args!("HTTP request failed with non-200 status code: {}", e.status)
92            },
93
94        Parse
95            {
96                reason: String
97            }
98            | e | {
99                format_args!("parse error: {}", e.reason)
100            },
101
102        Server
103            {
104                reason: String
105            }
106            | e | {
107                format_args!("server error: {}", e.reason)
108            },
109
110        ClientInternal
111            {
112                reason: String
113            }
114            | e | {
115                format_args!("client internal error: {}", e.reason)
116            },
117
118        Timeout
119            {
120                duration: Duration
121            }
122            | e | {
123                format_args!(
124                    "timed out waiting for healthy response after {}ms",
125                    e.duration.as_millis()
126                )
127            },
128
129        ChannelSend
130            | _ | { "failed to send message to internal channel" },
131
132        InvalidUrl
133            { url: Url }
134            | e | {
135                format_args!(
136                    "cannot use URL {} with HTTP clients",
137                    e.url
138                )
139            },
140
141        Tendermint
142            [ tendermint::Error ]
143            | _ | { "tendermint error" },
144
145        ParseInt
146            [ DisplayOnly<core::num::ParseIntError> ]
147            | _ | { "error parsing integer" },
148
149        OutOfRange
150            [ DisplayOnly<core::num::TryFromIntError> ]
151            | _ | { "number out of range" },
152
153        InvalidNetworkAddress
154            | _ | { "only TCP-based node addresses are supported" },
155
156        MismatchResponse
157            | _ | { "no matching response for incoming request" },
158
159        UnrecognizedEventType
160            {
161                event_type: String
162            }
163            | e | {
164                format_args!("unrecognized event type: {}", e.event_type)
165            },
166
167        Serde
168            [ DisplayOnly<serde_json::Error> ]
169            | _ | { "serde parse error" },
170
171        ParseUrl
172            [ DisplayOnly<url::ParseError> ]
173            | _ | { "parse error" },
174
175        Tungstenite
176            [ TungsteniteError ]
177            | _ | { "tungstenite error" },
178
179        Join
180            [ JoinError ]
181            | _ | { "join error" },
182
183        MalformedJson
184            | _ | { "server returned malformatted JSON (no 'result' or 'error')" },
185
186        UnsupportedScheme
187            {
188                scheme: String
189            }
190            | e | {
191                format_args!("unsupported scheme: {}", e.scheme)
192            },
193
194        UnsupportedRpcVersion
195            {
196                version: String,
197                supported: String
198            }
199            | e | {
200                format_args!("server RPC version unsupported: '{}' (only '{}' supported)",
201                    e.version, e.supported)
202            },
203
204        InvalidTendermintVersion
205            {
206                version: String,
207            }
208            | e | {
209                format_args!("invalid Tendermint version reported by the node: {}", e.version)
210            },
211
212        UnsupportedTendermintVersion
213            {
214                version: String,
215            }
216            | e | {
217                format_args!("unsupported Tendermint version reported by the node: {}", e.version)
218            },
219
220        InvalidCompatMode
221            {
222                mode: String,
223                supported: &'static str,
224            }
225            | e | {
226                format_args!("invalid compatibility mode: '{}' (supported: {})",
227                    e.mode, e.supported)
228            },
229    }
230}
231
232impl Clone for Error {
233    fn clone(&self) -> Self {
234        Error(
235            self.detail().clone(),
236            DefaultTracer::new_message(self.trace()),
237        )
238    }
239}
240
241#[cfg(feature = "tokio")]
242impl Error {
243    pub fn send<T>(_: tokio::sync::mpsc::error::SendError<T>) -> Error {
244        Error::channel_send()
245    }
246}