//! Connection errors and convenience types. use crate::{ driver::tasks::{error::Recipient, message::*}, ws::Error as WsError, }; use flume::SendError; use serde_json::Error as JsonError; use std::{error::Error as StdError, fmt, io::Error as IoError}; use tokio::time::error::Elapsed; use xsalsa20poly1305::aead::Error as CryptoError; /// Errors encountered while connecting to a Discord voice server over the driver. #[derive(Debug)] #[non_exhaustive] pub enum Error { /// The driver hung up an internal signaller, either due to another connection attempt /// or a crash. AttemptDiscarded, /// An error occurred during [en/de]cryption of voice packets. Crypto(CryptoError), /// The symmetric key supplied by Discord had the wrong size. CryptoInvalidLength, /// Server did not return the expected crypto mode during negotiation. CryptoModeInvalid, /// Selected crypto mode was not offered by server. CryptoModeUnavailable, /// An indicator that an endpoint URL was invalid. EndpointUrl, /// Discord failed to correctly respond to IP discovery. IllegalDiscoveryResponse, /// Could not parse Discord's view of our IP. IllegalIp, /// Miscellaneous I/O error. Io(IoError), /// JSON (de)serialization error. Json(JsonError), /// Failed to message other background tasks after connection establishment. InterconnectFailure(Recipient), /// Error communicating with gateway server over WebSocket. Ws(WsError), /// Connection attempt timed out. TimedOut, } impl From for Error { fn from(e: CryptoError) -> Self { Error::Crypto(e) } } impl From for Error { fn from(e: IoError) -> Error { Error::Io(e) } } impl From for Error { fn from(e: JsonError) -> Error { Error::Json(e) } } impl From> for Error { fn from(_e: SendError) -> Error { Error::InterconnectFailure(Recipient::AuxNetwork) } } impl From> for Error { fn from(_e: SendError) -> Error { Error::InterconnectFailure(Recipient::Event) } } impl From> for Error { fn from(_e: SendError) -> Error { Error::InterconnectFailure(Recipient::Mixer) } } impl From for Error { fn from(e: WsError) -> Error { Error::Ws(e) } } impl From for Error { fn from(_e: Elapsed) -> Error { Error::TimedOut } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "failed to connect to Discord RTP server: ")?; match self { Self::AttemptDiscarded => write!(f, "connection attempt was aborted/discarded"), Self::Crypto(e) => e.fmt(f), Self::CryptoInvalidLength => write!(f, "server supplied key of wrong length"), Self::CryptoModeInvalid => write!(f, "server changed negotiated encryption mode"), Self::CryptoModeUnavailable => write!(f, "server did not offer chosen encryption mode"), Self::EndpointUrl => write!(f, "endpoint URL received from gateway was invalid"), Self::IllegalDiscoveryResponse => write!(f, "IP discovery/NAT punching response was invalid"), Self::IllegalIp => write!(f, "IP discovery/NAT punching response had bad IP value"), Self::Io(e) => e.fmt(f), Self::Json(e) => e.fmt(f), Self::InterconnectFailure(e) => write!(f, "failed to contact other task ({e:?})"), Self::Ws(e) => write!(f, "websocket issue ({e:?})."), Self::TimedOut => write!(f, "connection attempt timed out"), } } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { Error::AttemptDiscarded | Error::CryptoInvalidLength | Error::CryptoModeInvalid | Error::CryptoModeUnavailable | Error::EndpointUrl | Error::IllegalDiscoveryResponse | Error::IllegalIp | Error::InterconnectFailure(_) | Error::Ws(_) | Error::TimedOut => None, Error::Crypto(e) => e.source(), Error::Io(e) => e.source(), Error::Json(e) => e.source(), } } } /// Convenience type for Discord voice/driver connection error handling. pub type Result = std::result::Result;