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}