titanium_gateway/
error.rs1use thiserror::Error;
7
8#[derive(Debug, Error)]
10pub enum GatewayError {
11 #[error("WebSocket error: {0}")]
13 WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
14
15 #[error("JSON decode error: {0}")]
17 JsonDecode(String),
18
19 #[error("Session invalidated, resumable: {resumable}")]
22 InvalidSession {
23 resumable: bool,
25 },
26
27 #[error("Connection closed: code={code}, reason={reason}")]
29 Closed {
30 code: u16,
32 reason: String,
34 },
35
36 #[error("Heartbeat acknowledgment timeout")]
38 HeartbeatTimeout,
39
40 #[error("Authentication failed: {0}")]
42 AuthenticationFailed(String),
43
44 #[error("Unknown event type: {0}")]
46 UnknownEvent(String),
47
48 #[error("Channel send error: {0}")]
50 ChannelSend(String),
51
52 #[error("URL parse error: {0}")]
54 UrlParse(#[from] url::ParseError),
55
56 #[error("I/O error: {0}")]
58 Io(#[from] std::io::Error),
59
60 #[error("Shard not connected")]
62 NotConnected,
63
64 #[error("Rate limited, retry after {retry_after_ms}ms")]
66 RateLimited {
67 retry_after_ms: u64,
69 },
70}
71
72impl From<serde_json::Error> for GatewayError {
73 fn from(err: serde_json::Error) -> Self {
74 GatewayError::JsonDecode(err.to_string())
75 }
76}
77
78#[cfg(feature = "simd")]
79impl From<simd_json::Error> for GatewayError {
80 fn from(err: simd_json::Error) -> Self {
81 GatewayError::JsonDecode(err.to_string())
82 }
83}
84
85impl<T> From<flume::SendError<T>> for GatewayError {
86 fn from(err: flume::SendError<T>) -> Self {
87 GatewayError::ChannelSend(err.to_string())
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95#[repr(u16)]
96pub enum CloseCode {
97 UnknownError = 4000,
99 UnknownOpcode = 4001,
101 DecodeError = 4002,
103 NotAuthenticated = 4003,
105 AuthenticationFailed = 4004,
107 AlreadyAuthenticated = 4005,
109 InvalidSeq = 4007,
111 RateLimited = 4008,
113 SessionTimedOut = 4009,
115 InvalidShard = 4010,
117 ShardingRequired = 4011,
119 InvalidApiVersion = 4012,
121 InvalidIntents = 4013,
123 DisallowedIntents = 4014,
125}
126
127impl CloseCode {
128 #[allow(dead_code)]
130 pub const fn is_resumable(self) -> bool {
131 matches!(
132 self,
133 CloseCode::UnknownError | CloseCode::InvalidSeq | CloseCode::SessionTimedOut
134 )
135 }
136
137 pub const fn can_reconnect(self) -> bool {
139 !matches!(
140 self,
141 CloseCode::AuthenticationFailed
142 | CloseCode::InvalidShard
143 | CloseCode::ShardingRequired
144 | CloseCode::InvalidApiVersion
145 | CloseCode::InvalidIntents
146 | CloseCode::DisallowedIntents
147 )
148 }
149
150 pub fn from_code(code: u16) -> Option<Self> {
152 match code {
153 4000 => Some(CloseCode::UnknownError),
154 4001 => Some(CloseCode::UnknownOpcode),
155 4002 => Some(CloseCode::DecodeError),
156 4003 => Some(CloseCode::NotAuthenticated),
157 4004 => Some(CloseCode::AuthenticationFailed),
158 4005 => Some(CloseCode::AlreadyAuthenticated),
159 4007 => Some(CloseCode::InvalidSeq),
160 4008 => Some(CloseCode::RateLimited),
161 4009 => Some(CloseCode::SessionTimedOut),
162 4010 => Some(CloseCode::InvalidShard),
163 4011 => Some(CloseCode::ShardingRequired),
164 4012 => Some(CloseCode::InvalidApiVersion),
165 4013 => Some(CloseCode::InvalidIntents),
166 4014 => Some(CloseCode::DisallowedIntents),
167 _ => None,
168 }
169 }
170}