skua_voice/driver/connection/
error.rs

1//! Connection errors and convenience types.
2
3use crate::{
4    driver::tasks::{error::Recipient, message::*},
5    ws::Error as WsError,
6};
7use crypto_secretbox::Error as CryptoError;
8use flume::SendError;
9use serde_json::Error as JsonError;
10use std::{error::Error as StdError, fmt, io::Error as IoError};
11use tokio::time::error::Elapsed;
12
13/// Errors encountered while connecting to a Discord voice server over the driver.
14#[derive(Debug)]
15#[non_exhaustive]
16pub enum Error {
17    /// The driver hung up an internal signaller, either due to another connection attempt
18    /// or a crash.
19    AttemptDiscarded,
20    /// An error occurred during [en/de]cryption of voice packets.
21    Crypto(CryptoError),
22    /// The symmetric key supplied by Discord had the wrong size.
23    CryptoInvalidLength,
24    /// Server did not return the expected crypto mode during negotiation.
25    CryptoModeInvalid,
26    /// Selected crypto mode was not offered by server.
27    CryptoModeUnavailable,
28    /// An indicator that an endpoint URL was invalid.
29    EndpointUrl,
30    /// Discord failed to correctly respond to IP discovery.
31    IllegalDiscoveryResponse,
32    /// Could not parse Discord's view of our IP.
33    IllegalIp,
34    /// Miscellaneous I/O error.
35    Io(IoError),
36    /// JSON (de)serialization error.
37    Json(JsonError),
38    /// Failed to message other background tasks after connection establishment.
39    InterconnectFailure(Recipient),
40    /// Error communicating with gateway server over WebSocket.
41    Ws(WsError),
42    /// Connection attempt timed out.
43    TimedOut,
44}
45
46impl From<CryptoError> for Error {
47    fn from(e: CryptoError) -> Self {
48        Error::Crypto(e)
49    }
50}
51
52impl From<IoError> for Error {
53    fn from(e: IoError) -> Error {
54        Error::Io(e)
55    }
56}
57
58impl From<JsonError> for Error {
59    fn from(e: JsonError) -> Error {
60        Error::Json(e)
61    }
62}
63
64impl From<SendError<WsMessage>> for Error {
65    fn from(_e: SendError<WsMessage>) -> Error {
66        Error::InterconnectFailure(Recipient::AuxNetwork)
67    }
68}
69
70impl From<SendError<EventMessage>> for Error {
71    fn from(_e: SendError<EventMessage>) -> Error {
72        Error::InterconnectFailure(Recipient::Event)
73    }
74}
75
76impl From<SendError<MixerMessage>> for Error {
77    fn from(_e: SendError<MixerMessage>) -> Error {
78        Error::InterconnectFailure(Recipient::Mixer)
79    }
80}
81
82impl From<WsError> for Error {
83    fn from(e: WsError) -> Error {
84        Error::Ws(e)
85    }
86}
87
88impl From<Elapsed> for Error {
89    fn from(_e: Elapsed) -> Error {
90        Error::TimedOut
91    }
92}
93
94impl fmt::Display for Error {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "failed to connect to Discord RTP server: ")?;
97        match self {
98            Self::AttemptDiscarded => write!(f, "connection attempt was aborted/discarded"),
99            Self::Crypto(e) => e.fmt(f),
100            Self::CryptoInvalidLength => write!(f, "server supplied key of wrong length"),
101            Self::CryptoModeInvalid => write!(f, "server changed negotiated encryption mode"),
102            Self::CryptoModeUnavailable => write!(f, "server did not offer chosen encryption mode"),
103            Self::EndpointUrl => write!(f, "endpoint URL received from gateway was invalid"),
104            Self::IllegalDiscoveryResponse =>
105                write!(f, "IP discovery/NAT punching response was invalid"),
106            Self::IllegalIp => write!(f, "IP discovery/NAT punching response had bad IP value"),
107            Self::Io(e) => e.fmt(f),
108            Self::Json(e) => e.fmt(f),
109            Self::InterconnectFailure(e) => write!(f, "failed to contact other task ({e:?})"),
110            Self::Ws(e) => write!(f, "websocket issue ({e:?})."),
111            Self::TimedOut => write!(f, "connection attempt timed out"),
112        }
113    }
114}
115
116impl StdError for Error {
117    fn source(&self) -> Option<&(dyn StdError + 'static)> {
118        match self {
119            Error::AttemptDiscarded
120            | Error::CryptoInvalidLength
121            | Error::CryptoModeInvalid
122            | Error::CryptoModeUnavailable
123            | Error::EndpointUrl
124            | Error::IllegalDiscoveryResponse
125            | Error::IllegalIp
126            | Error::InterconnectFailure(_)
127            | Error::Ws(_)
128            | Error::TimedOut => None,
129            Error::Crypto(e) => e.source(),
130            Error::Io(e) => e.source(),
131            Error::Json(e) => e.source(),
132        }
133    }
134}
135
136/// Convenience type for Discord voice/driver connection error handling.
137pub type Result<T> = std::result::Result<T, Error>;