skua_voice/
error.rs

1//! Driver and gateway error handling.
2
3#[cfg(feature = "skua")]
4use futures::channel::mpsc::TrySendError;
5#[cfg(not(feature = "simd-json"))]
6pub use serde_json::Error as JsonError;
7#[cfg(feature = "skua")]
8use skua::gateway::ShardRunnerMessage;
9#[cfg(feature = "simd-json")]
10pub use simd_json::Error as JsonError;
11#[cfg(feature = "gateway")]
12use std::{error::Error, fmt};
13#[cfg(feature = "twilight")]
14use twilight_gateway::error::SendError;
15
16#[cfg(feature = "gateway")]
17#[derive(Debug)]
18#[non_exhaustive]
19/// Error returned when a manager or call handler is
20/// unable to send messages over Discord's gateway.
21pub enum JoinError {
22    /// Request to join was dropped, cancelled, or replaced.
23    Dropped,
24    /// No available gateway connection was provided to send
25    /// voice state update messages.
26    NoSender,
27    /// Tried to leave a [`Call`] which was not found.
28    ///
29    /// [`Call`]: crate::Call
30    NoCall,
31    /// Connection details were not received from Discord in the
32    /// time given in [the `Call`'s configuration].
33    ///
34    /// This can occur if a message is lost by the Discord client
35    /// between restarts, or if Discord's gateway believes that
36    /// this bot is still in the channel it attempts to join.
37    ///
38    /// *Users should `leave` the server on the gateway before
39    /// re-attempting connection.*
40    ///
41    /// [the `Call`'s configuration]: crate::Config
42    TimedOut,
43    #[cfg(feature = "driver")]
44    /// The driver failed to establish a voice connection.
45    ///
46    /// *Users should `leave` the server on the gateway before
47    /// re-attempting connection.*
48    Driver(ConnectionError),
49    #[cfg(feature = "skua")]
50    /// skua-specific WebSocket send error.
51    Skua(Box<TrySendError<ShardRunnerMessage>>),
52    #[cfg(feature = "twilight")]
53    /// Twilight-specific WebSocket send error when a message fails to send over websocket.
54    Twilight(SendError),
55}
56
57#[cfg(feature = "gateway")]
58impl JoinError {
59    /// Indicates whether this failure may have left (or been
60    /// caused by) Discord's gateway state being in an
61    /// inconsistent state.
62    ///
63    /// Failure to `leave` before rejoining may cause further
64    /// timeouts.
65    pub fn should_leave_server(&self) -> bool {
66        matches!(self, JoinError::TimedOut)
67    }
68
69    #[cfg(feature = "driver")]
70    /// Indicates whether this failure can be reattempted via
71    /// [`Driver::connect`] with retreived connection info.
72    ///
73    /// Failure to `leave` before rejoining may cause further
74    /// timeouts.
75    ///
76    /// [`Driver::connect`]: crate::driver::Driver
77    pub fn should_reconnect_driver(&self) -> bool {
78        matches!(self, JoinError::Driver(_))
79    }
80}
81
82#[cfg(feature = "gateway")]
83impl fmt::Display for JoinError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "failed to join voice channel: ")?;
86        match self {
87            JoinError::Dropped => write!(f, "request was cancelled/dropped"),
88            JoinError::NoSender => write!(f, "no gateway destination"),
89            JoinError::NoCall => write!(f, "tried to leave a non-existent call"),
90            JoinError::TimedOut => write!(f, "gateway response from Discord timed out"),
91            #[cfg(feature = "driver")]
92            JoinError::Driver(_) => write!(f, "establishing connection failed"),
93            #[cfg(feature = "skua")]
94            JoinError::Skua(e) => e.fmt(f),
95            #[cfg(feature = "twilight")]
96            JoinError::Twilight(e) => e.fmt(f),
97        }
98    }
99}
100
101#[cfg(feature = "gateway")]
102impl Error for JoinError {
103    fn source(&self) -> Option<&(dyn Error + 'static)> {
104        match self {
105            JoinError::Dropped => None,
106            JoinError::NoSender => None,
107            JoinError::NoCall => None,
108            JoinError::TimedOut => None,
109            #[cfg(feature = "driver")]
110            JoinError::Driver(e) => Some(e),
111            #[cfg(feature = "skua")]
112            JoinError::Skua(e) => e.source(),
113            #[cfg(feature = "twilight")]
114            JoinError::Twilight(e) => e.source(),
115        }
116    }
117}
118
119#[cfg(all(feature = "skua", feature = "gateway"))]
120impl From<Box<TrySendError<ShardRunnerMessage>>> for JoinError {
121    fn from(e: Box<TrySendError<ShardRunnerMessage>>) -> Self {
122        JoinError::Skua(e)
123    }
124}
125
126#[cfg(all(feature = "twilight", feature = "gateway"))]
127impl From<SendError> for JoinError {
128    fn from(e: SendError) -> Self {
129        JoinError::Twilight(e)
130    }
131}
132
133#[cfg(all(feature = "driver", feature = "gateway"))]
134impl From<ConnectionError> for JoinError {
135    fn from(e: ConnectionError) -> Self {
136        JoinError::Driver(e)
137    }
138}
139
140#[cfg(feature = "gateway")]
141/// Convenience type for Discord gateway error handling.
142pub type JoinResult<T> = Result<T, JoinError>;
143
144#[cfg(feature = "driver")]
145pub use crate::{
146    driver::{
147        connection::error::{Error as ConnectionError, Result as ConnectionResult},
148        SchedulerError,
149    },
150    tracks::{ControlError, PlayError, TrackResult},
151};