Skip to main content

twilight_gateway/
error.rs

1//! Errors returned by gateway operations.
2
3#[cfg(any(feature = "zlib", feature = "zstd"))]
4pub use crate::compression::{CompressionError, CompressionErrorType};
5
6use std::{
7    error::Error,
8    fmt::{Debug, Display, Formatter, Result as FmtResult},
9};
10
11/// Sending a command over a channel failed.
12#[derive(Debug)]
13pub struct ChannelError {
14    /// Type of error.
15    pub(crate) kind: ChannelErrorType,
16    /// Source error if available.
17    pub(crate) source: Option<Box<dyn Error + Send + Sync>>,
18}
19
20impl ChannelError {
21    /// Immutable reference to the type of error that occurred.
22    #[must_use = "retrieving the type has no effect if left unused"]
23    pub const fn kind(&self) -> &ChannelErrorType {
24        &self.kind
25    }
26
27    /// Consume the error, returning the source error if there is any.
28    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
29    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
30        self.source
31    }
32
33    /// Consume the error, returning the owned error type and the source error.
34    #[must_use = "consuming the error into its parts has no effect if left unused"]
35    pub fn into_parts(self) -> (ChannelErrorType, Option<Box<dyn Error + Send + Sync>>) {
36        (self.kind, None)
37    }
38}
39
40impl Display for ChannelError {
41    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
42        match self.kind {
43            ChannelErrorType::Closed => f.write_str("tried sending over a closed channel"),
44        }
45    }
46}
47
48impl Error for ChannelError {
49    fn source(&self) -> Option<&(dyn Error + 'static)> {
50        self.source
51            .as_ref()
52            .map(|source| &**source as &(dyn Error + 'static))
53    }
54}
55
56/// Type of [`ChannelError`] that occurred.
57#[derive(Debug)]
58#[non_exhaustive]
59pub enum ChannelErrorType {
60    /// Tried sending over a closed channel.
61    Closed,
62}
63
64/// Failure when fetching the recommended number of shards to use from Discord's
65/// REST API.
66#[cfg(feature = "twilight-http")]
67#[deprecated(since = "0.17.2")]
68#[derive(Debug)]
69pub struct StartRecommendedError {
70    /// Type of error.
71    #[allow(deprecated)]
72    pub(crate) kind: StartRecommendedErrorType,
73    /// Source error if available.
74    pub(crate) source: Option<Box<dyn Error + Send + Sync>>,
75}
76
77#[allow(deprecated)]
78#[cfg(feature = "twilight-http")]
79impl StartRecommendedError {
80    /// Immutable reference to the type of error that occurred.
81    #[must_use = "retrieving the type has no effect if left unused"]
82    pub const fn kind(&self) -> &StartRecommendedErrorType {
83        &self.kind
84    }
85
86    /// Consume the error, returning the source error if there is any.
87    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
88    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
89        self.source
90    }
91
92    /// Consume the error, returning the owned error type and the source error.
93    #[must_use = "consuming the error into its parts has no effect if left unused"]
94    pub fn into_parts(
95        self,
96    ) -> (
97        StartRecommendedErrorType,
98        Option<Box<dyn Error + Send + Sync>>,
99    ) {
100        (self.kind, None)
101    }
102}
103
104#[allow(deprecated)]
105#[cfg(feature = "twilight-http")]
106impl Display for StartRecommendedError {
107    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
108        match self.kind {
109            StartRecommendedErrorType::Deserializing => {
110                f.write_str("payload isn't a recognized type")
111            }
112            StartRecommendedErrorType::Request => f.write_str("request failed to complete"),
113        }
114    }
115}
116
117#[allow(deprecated)]
118#[cfg(feature = "twilight-http")]
119impl Error for StartRecommendedError {
120    fn source(&self) -> Option<&(dyn Error + 'static)> {
121        self.source
122            .as_ref()
123            .map(|source| &**source as &(dyn Error + 'static))
124    }
125}
126
127/// Type of [`StartRecommendedError`] that occurred.
128#[cfg(feature = "twilight-http")]
129#[deprecated(since = "0.17.2")]
130#[derive(Debug)]
131pub enum StartRecommendedErrorType {
132    /// Received gateway event failed to be deserialized.
133    ///
134    /// The message payload is likely an unrecognized type that is not yet
135    /// supported.
136    Deserializing,
137    /// Requesting recommended shards from Discord's REST API failed.
138    ///
139    /// May be due to something such as a network or authentication issue.
140    Request,
141}
142
143/// Receiving the next Websocket message failed.
144#[derive(Debug)]
145pub struct ReceiveMessageError {
146    /// Type of error.
147    pub(crate) kind: ReceiveMessageErrorType,
148    /// Source error if available.
149    pub(crate) source: Option<Box<dyn Error + Send + Sync>>,
150}
151
152impl ReceiveMessageError {
153    /// Immutable reference to the type of error that occurred.
154    #[must_use = "retrieving the type has no effect if left unused"]
155    pub const fn kind(&self) -> &ReceiveMessageErrorType {
156        &self.kind
157    }
158
159    /// Consume the error, returning the source error if there is any.
160    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
161    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
162        self.source
163    }
164
165    /// Consume the error, returning the owned error type and the source error.
166    #[must_use = "consuming the error into its parts has no effect if left unused"]
167    pub fn into_parts(
168        self,
169    ) -> (
170        ReceiveMessageErrorType,
171        Option<Box<dyn Error + Send + Sync>>,
172    ) {
173        (self.kind, None)
174    }
175
176    /// Shortcut to create a new error for a message compression error.
177    #[cfg(any(feature = "zlib", feature = "zstd"))]
178    pub(crate) fn from_compression(source: CompressionError) -> Self {
179        Self {
180            kind: ReceiveMessageErrorType::Compression,
181            source: Some(Box::new(source)),
182        }
183    }
184}
185
186impl Display for ReceiveMessageError {
187    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
188        match &self.kind {
189            #[cfg(any(feature = "zlib", feature = "zstd"))]
190            ReceiveMessageErrorType::Compression => {
191                f.write_str("binary message could not be decompressed")
192            }
193            ReceiveMessageErrorType::Deserializing { event } => {
194                f.write_str("gateway event could not be deserialized: event=")?;
195                f.write_str(event)
196            }
197            ReceiveMessageErrorType::Reconnect => f.write_str("failed to reconnect to the gateway"),
198        }
199    }
200}
201
202impl Error for ReceiveMessageError {
203    fn source(&self) -> Option<&(dyn Error + 'static)> {
204        self.source
205            .as_ref()
206            .map(|source| &**source as &(dyn Error + 'static))
207    }
208}
209
210/// Type of [`ReceiveMessageError`] that occurred.
211#[derive(Debug)]
212#[non_exhaustive]
213pub enum ReceiveMessageErrorType {
214    /// Binary message could not be decompressed.
215    ///
216    /// The associated error downcasts to [`CompressionError`].
217    #[cfg(any(feature = "zlib", feature = "zstd"))]
218    Compression,
219    /// Gateway event could not be deserialized.
220    Deserializing {
221        /// Gateway event.
222        ///
223        /// Note that the `simd-json` feature may slightly modify the event.
224        event: String,
225    },
226    /// Shard failed to reconnect to the gateway.
227    Reconnect,
228}
229
230#[cfg(test)]
231mod tests {
232    use super::{ReceiveMessageError, ReceiveMessageErrorType};
233    use static_assertions::assert_impl_all;
234    use std::{error::Error, fmt::Debug};
235
236    assert_impl_all!(ReceiveMessageErrorType: Debug, Send, Sync);
237    assert_impl_all!(ReceiveMessageError: Error, Send, Sync);
238
239    #[test]
240    fn receive_message_error_display() {
241        let messages: [(ReceiveMessageErrorType, &str); 3] = [
242            (
243                ReceiveMessageErrorType::Compression,
244                "binary message could not be decompressed",
245            ),
246            (
247                ReceiveMessageErrorType::Deserializing {
248                    event: r#"{"t":null,"s":null,"op":10,"d":{"heartbeat_interval":41250,"_trace":["[\"gateway-prd-us-east1-b-0568\",{\"micros\":0.0}]"]}}"#.to_owned(),
249                },
250                r#"gateway event could not be deserialized: event={"t":null,"s":null,"op":10,"d":{"heartbeat_interval":41250,"_trace":["[\"gateway-prd-us-east1-b-0568\",{\"micros\":0.0}]"]}}"#,
251            ),
252            (
253                ReceiveMessageErrorType::Reconnect,
254                "failed to reconnect to the gateway",
255            ),
256        ];
257
258        for (kind, message) in messages {
259            let error = ReceiveMessageError { kind, source: None };
260
261            assert_eq!(error.to_string(), message);
262        }
263    }
264}