use serde_repr::{Deserialize_repr, Serialize_repr};
use std::convert::TryFrom;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
#[derive(
Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize_repr,
)]
#[non_exhaustive]
#[repr(u16)]
pub enum CloseCode {
UnknownOpcode = 4001,
DecodeError = 4002,
NotAuthenticated = 4003,
AuthenticationFailed = 4004,
AlreadyAuthenticated = 4005,
SessionNoLongerValid = 4006,
SessionTimedOut = 4009,
ServerNotFound = 4011,
UnknownProtocol = 4012,
Disconnected = 4014,
VoiceServerCrashed = 4015,
UnknownEncryptionMode = 4016,
}
#[derive(Debug, PartialEq)]
pub struct CloseCodeConversionError {
code: u16,
}
impl CloseCodeConversionError {
const fn new(code: u16) -> Self {
Self { code }
}
pub const fn code(&self) -> u16 {
self.code
}
}
impl Display for CloseCodeConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_fmt(format_args!("{} isn't a valid close code", self.code))
}
}
impl Error for CloseCodeConversionError {}
impl TryFrom<u16> for CloseCode {
type Error = CloseCodeConversionError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
let close_code = match value {
4001 => CloseCode::UnknownOpcode,
4002 => CloseCode::DecodeError,
4003 => CloseCode::NotAuthenticated,
4004 => CloseCode::AuthenticationFailed,
4005 => CloseCode::AlreadyAuthenticated,
4006 => CloseCode::SessionNoLongerValid,
4009 => CloseCode::SessionTimedOut,
4011 => CloseCode::ServerNotFound,
4012 => CloseCode::UnknownProtocol,
4014 => CloseCode::Disconnected,
4015 => CloseCode::VoiceServerCrashed,
4016 => CloseCode::UnknownEncryptionMode,
_ => return Err(CloseCodeConversionError::new(value)),
};
Ok(close_code)
}
}
#[cfg(test)]
mod tests {
use super::CloseCode;
use serde_test::Token;
use std::convert::TryFrom;
#[test]
fn test_variants() {
serde_test::assert_tokens(&CloseCode::UnknownOpcode, &[Token::U16(4001)]);
serde_test::assert_tokens(&CloseCode::DecodeError, &[Token::U16(4002)]);
serde_test::assert_tokens(&CloseCode::NotAuthenticated, &[Token::U16(4003)]);
serde_test::assert_tokens(&CloseCode::AuthenticationFailed, &[Token::U16(4004)]);
serde_test::assert_tokens(&CloseCode::AlreadyAuthenticated, &[Token::U16(4005)]);
serde_test::assert_tokens(&CloseCode::SessionTimedOut, &[Token::U16(4009)]);
serde_test::assert_tokens(&CloseCode::ServerNotFound, &[Token::U16(4011)]);
serde_test::assert_tokens(&CloseCode::UnknownProtocol, &[Token::U16(4012)]);
serde_test::assert_tokens(&CloseCode::Disconnected, &[Token::U16(4014)]);
serde_test::assert_tokens(&CloseCode::VoiceServerCrashed, &[Token::U16(4015)]);
serde_test::assert_tokens(&CloseCode::UnknownEncryptionMode, &[Token::U16(4016)]);
}
#[test]
fn test_conversion() {
assert_eq!(CloseCode::try_from(4001).unwrap(), CloseCode::UnknownOpcode);
assert_eq!(CloseCode::try_from(4002).unwrap(), CloseCode::DecodeError);
assert_eq!(
CloseCode::try_from(4003).unwrap(),
CloseCode::NotAuthenticated
);
assert_eq!(
CloseCode::try_from(4004).unwrap(),
CloseCode::AuthenticationFailed
);
assert_eq!(
CloseCode::try_from(4005).unwrap(),
CloseCode::AlreadyAuthenticated
);
assert_eq!(
CloseCode::try_from(4009).unwrap(),
CloseCode::SessionTimedOut
);
assert_eq!(
CloseCode::try_from(4011).unwrap(),
CloseCode::ServerNotFound
);
assert_eq!(
CloseCode::try_from(4012).unwrap(),
CloseCode::UnknownProtocol
);
assert_eq!(CloseCode::try_from(4014).unwrap(), CloseCode::Disconnected);
assert_eq!(
CloseCode::try_from(4015).unwrap(),
CloseCode::VoiceServerCrashed
);
assert_eq!(
CloseCode::try_from(4016).unwrap(),
CloseCode::UnknownEncryptionMode
);
assert!(CloseCode::try_from(5000).is_err());
}
}