Skip to main content

tokio_centrifuge/
errors.rs

1use std::borrow::Cow;
2use std::fmt::Display;
3
4use thiserror::Error;
5use tokio_tungstenite::tungstenite::protocol::CloseFrame;
6
7#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
8pub enum RemoveSubscriptionError {
9    #[error("subscription must be unsubscribed to be removed")]
10    NotUnsubscribed,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct ClientError {
15    pub code: ClientErrorCode,
16    pub message: Cow<'static, str>,
17}
18
19impl From<anyhow::Error> for ClientError {
20    fn from(err: anyhow::Error) -> Self {
21        Self::internal(err.to_string())
22    }
23}
24
25impl From<ClientError> for crate::protocol::Error {
26    fn from(err: ClientError) -> Self {
27        Self {
28            code: err.code.0.into(),
29            message: err.message.into_owned(),
30            temporary: err.code.is_temporary(),
31        }
32    }
33}
34
35impl ClientError {
36    pub fn new(code: ClientErrorCode, message: impl Into<Cow<'static, str>>) -> Self {
37        Self {
38            code,
39            message: message.into(),
40        }
41    }
42
43    pub fn internal(message: impl Into<Cow<'static, str>>) -> Self {
44        Self::new(ClientErrorCode::Internal, message)
45    }
46
47    pub fn unauthorized(message: impl Into<Cow<'static, str>>) -> Self {
48        Self::new(ClientErrorCode::Unauthorized, message)
49    }
50
51    pub fn unknown_channel(message: impl Into<Cow<'static, str>>) -> Self {
52        Self::new(ClientErrorCode::UnknownChannel, message)
53    }
54
55    pub fn permission_denied(message: impl Into<Cow<'static, str>>) -> Self {
56        Self::new(ClientErrorCode::PermissionDenied, message)
57    }
58
59    pub fn method_not_found(message: impl Into<Cow<'static, str>>) -> Self {
60        Self::new(ClientErrorCode::MethodNotFound, message)
61    }
62
63    pub fn already_subscribed(message: impl Into<Cow<'static, str>>) -> Self {
64        Self::new(ClientErrorCode::AlreadySubscribed, message)
65    }
66
67    pub fn limit_exceeded(message: impl Into<Cow<'static, str>>) -> Self {
68        Self::new(ClientErrorCode::LimitExceeded, message)
69    }
70
71    pub fn bad_request(message: impl Into<Cow<'static, str>>) -> Self {
72        Self::new(ClientErrorCode::BadRequest, message)
73    }
74
75    pub fn not_available(message: impl Into<Cow<'static, str>>) -> Self {
76        Self::new(ClientErrorCode::NotAvailable, message)
77    }
78
79    pub fn token_expired(message: impl Into<Cow<'static, str>>) -> Self {
80        Self::new(ClientErrorCode::TokenExpired, message)
81    }
82
83    pub fn expired(message: impl Into<Cow<'static, str>>) -> Self {
84        Self::new(ClientErrorCode::Expired, message)
85    }
86
87    pub fn too_many_requests(message: impl Into<Cow<'static, str>>) -> Self {
88        Self::new(ClientErrorCode::TooManyRequests, message)
89    }
90
91    pub fn unrecoverable_position(message: impl Into<Cow<'static, str>>) -> Self {
92        Self::new(ClientErrorCode::UnrecoverablePosition, message)
93    }
94}
95
96impl From<ClientErrorCode> for ClientError {
97    fn from(code: ClientErrorCode) -> Self {
98        Self::new(code, code.to_str())
99    }
100}
101
102#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
103pub struct ClientErrorCode(pub u16);
104
105#[allow(non_upper_case_globals)]
106impl ClientErrorCode {
107    pub const Internal:              Self = Self(100);
108    pub const Unauthorized:          Self = Self(101);
109    pub const UnknownChannel:        Self = Self(102);
110    pub const PermissionDenied:      Self = Self(103);
111    pub const MethodNotFound:        Self = Self(104);
112    pub const AlreadySubscribed:     Self = Self(105);
113    pub const LimitExceeded:         Self = Self(106);
114    pub const BadRequest:            Self = Self(107);
115    pub const NotAvailable:          Self = Self(108);
116    pub const TokenExpired:          Self = Self(109);
117    pub const Expired:               Self = Self(110);
118    pub const TooManyRequests:       Self = Self(111);
119    pub const UnrecoverablePosition: Self = Self(112);
120
121    pub fn is_temporary(self) -> bool {
122        matches!(self, Self::Internal | Self::TooManyRequests)
123    }
124
125    pub fn to_str(&self) -> Cow<'static, str> {
126        match self.0 {
127            100 => Cow::Borrowed("internal server error"),
128            101 => Cow::Borrowed("unauthorized"),
129            102 => Cow::Borrowed("unknown channel"),
130            103 => Cow::Borrowed("permission denied"),
131            104 => Cow::Borrowed("method not found"),
132            105 => Cow::Borrowed("already subscribed"),
133            106 => Cow::Borrowed("limit exceeded"),
134            107 => Cow::Borrowed("bad request"),
135            108 => Cow::Borrowed("not available"),
136            109 => Cow::Borrowed("token expired"),
137            110 => Cow::Borrowed("expired"),
138            111 => Cow::Borrowed("too many requests"),
139            112 => Cow::Borrowed("unrecoverable position"),
140            _ => Cow::Owned(format!("unknown code {}", self.0)),
141        }
142    }
143}
144
145impl Display for ClientErrorCode {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        write!(f, "{}", self.to_str())
148    }
149}
150
151impl From<ClientErrorCode> for u16 {
152    fn from(code: ClientErrorCode) -> Self {
153        code.0
154    }
155}
156
157impl From<u16> for ClientErrorCode {
158    fn from(code: u16) -> Self {
159        Self(code)
160    }
161}
162
163impl From<ClientErrorCode> for crate::protocol::Error {
164    fn from(code: ClientErrorCode) -> Self {
165        Self {
166            code: code.0.into(),
167            message: code.to_string(),
168            temporary: code.is_temporary(),
169        }
170    }
171}
172
173#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
174pub struct DisconnectErrorCode(pub u16);
175
176#[allow(non_upper_case_globals)]
177impl DisconnectErrorCode {
178    pub const ConnectionClosed:       Self = Self(3000);
179    pub const Shutdown:               Self = Self(3001);
180    pub const ServerError:            Self = Self(3004);
181    pub const Expired:                Self = Self(3005);
182    pub const SubExpired:             Self = Self(3006);
183    pub const Slow:                   Self = Self(3008);
184    pub const WriteError:             Self = Self(3009);
185    pub const InsufficientState:      Self = Self(3010);
186    pub const ForceReconnect:         Self = Self(3011);
187    pub const NoPong:                 Self = Self(3012);
188    pub const TooManyRequests:        Self = Self(3013);
189    pub const InvalidToken:           Self = Self(3500);
190    pub const BadRequest:             Self = Self(3501);
191    pub const Stale:                  Self = Self(3502);
192    pub const ForceNoReconnect:       Self = Self(3503);
193    pub const ConnectionLimit:        Self = Self(3504);
194    pub const ChannelLimit:           Self = Self(3505);
195    pub const InappropriateProtocol:  Self = Self(3506);
196    pub const PermissionDenied:       Self = Self(3507);
197    pub const NotAvailable:           Self = Self(3508);
198    pub const TooManyErrors:          Self = Self(3509);
199
200    pub fn should_reconnect(self) -> bool {
201        !matches!(self.0, 3500..=3999 | 4500..=4999)
202    }
203}
204
205impl Display for DisconnectErrorCode {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        match self.0 {
208            3000 => write!(f, "connection closed"),
209            3001 => write!(f, "shutdown"),
210            3004 => write!(f, "internal server error"),
211            3005 => write!(f, "connection expired"),
212            3006 => write!(f, "subscription expired"),
213            3008 => write!(f, "slow"),
214            3009 => write!(f, "write error"),
215            3010 => write!(f, "insufficient state"),
216            3011 => write!(f, "force reconnect"),
217            3012 => write!(f, "no pong"),
218            3013 => write!(f, "too many requests"),
219            3500 => write!(f, "invalid token"),
220            3501 => write!(f, "bad request"),
221            3502 => write!(f, "stale"),
222            3503 => write!(f, "force disconnect"),
223            3504 => write!(f, "connection limit"),
224            3505 => write!(f, "channel limit"),
225            3506 => write!(f, "inappropriate protocol"),
226            3507 => write!(f, "permission denied"),
227            3508 => write!(f, "not available"),
228            3509 => write!(f, "too many errors"),
229            _ => write!(f, "unknown code {}", self.0),
230        }
231    }
232}
233
234impl From<DisconnectErrorCode> for u16 {
235    fn from(code: DisconnectErrorCode) -> Self {
236        code.0
237    }
238}
239
240impl From<u16> for DisconnectErrorCode {
241    fn from(code: u16) -> Self {
242        Self(code)
243    }
244}
245
246impl From<DisconnectErrorCode> for Option<CloseFrame> {
247    fn from(code: DisconnectErrorCode) -> Self {
248        Some(CloseFrame {
249            code: code.0.into(),
250            reason: code.to_string().into(),
251        })
252    }
253}