1use std::{
4 convert::{From, Into},
5 fmt,
6};
7
8use crate::ProtocolError;
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
11pub enum OpCode {
12 Data(OpCodeData),
14 Control(OpCodeControl),
16}
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
20pub enum OpCodeData {
21 Continue,
23 Text,
25 Binary,
27 Reserved(u8),
29}
30
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33pub enum OpCodeControl {
34 Close,
36 Ping,
38 Pong,
40 Reserved(u8),
42}
43
44impl fmt::Display for OpCodeData {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 match *self {
47 Self::Continue => write!(f, "CONTINUE"),
48 Self::Text => write!(f, "TEXT"),
49 Self::Binary => write!(f, "BINARY"),
50 Self::Reserved(x) => write!(f, "RESERVED_DATA_{x}"),
51 }
52 }
53}
54
55impl fmt::Display for OpCodeControl {
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 match *self {
58 Self::Close => write!(f, "CLOSE"),
59 Self::Ping => write!(f, "PING"),
60 Self::Pong => write!(f, "PONG"),
61 Self::Reserved(x) => write!(f, "RESERVED_CONTROL_{x}"),
62 }
63 }
64}
65
66impl fmt::Display for OpCode {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match *self {
69 Self::Data(d) => d.fmt(f),
70 Self::Control(c) => c.fmt(f),
71 }
72 }
73}
74
75impl From<OpCode> for u8 {
76 fn from(code: OpCode) -> Self {
77 use self::{
78 OpCodeControl::{Close, Ping, Pong},
79 OpCodeData::{Binary, Continue, Text},
80 };
81 match code {
82 OpCode::Data(Continue) => 0,
83 OpCode::Data(Text) => 1,
84 OpCode::Data(Binary) => 2,
85
86 OpCode::Control(Close) => 8,
87 OpCode::Control(Ping) => 9,
88 OpCode::Control(Pong) => 10,
89
90 OpCode::Data(self::OpCodeData::Reserved(i))
91 | OpCode::Control(self::OpCodeControl::Reserved(i)) => i,
92 }
93 }
94}
95
96impl TryFrom<u8> for OpCode {
97 type Error = ProtocolError;
98
99 fn try_from(byte: u8) -> Result<Self, Self::Error> {
100 use self::{
101 OpCodeControl::{Close, Ping, Pong},
102 OpCodeData::{Binary, Continue, Text},
103 };
104 Ok(match byte {
105 0 => Self::Data(Continue),
106 1 => Self::Data(Text),
107 2 => Self::Data(Binary),
108 i @ 3..=7 => Self::Data(self::OpCodeData::Reserved(i)),
109 8 => Self::Control(Close),
110 9 => Self::Control(Ping),
111 10 => Self::Control(Pong),
112 i @ 11..=15 => Self::Control(self::OpCodeControl::Reserved(i)),
113 _ => return Err(ProtocolError::InvalidOpcode(byte)),
114 })
115 }
116}
117
118#[derive(Debug, Eq, PartialEq, Clone, Copy)]
120#[allow(clippy::manual_non_exhaustive)]
121pub enum CloseCode {
122 Normal,
125 Away,
128 Protocol,
131 Unsupported,
136 Status,
140 Abnormal,
146 Invalid,
151 Policy,
157 Size,
161 Extension,
169 Error,
173 Restart,
176 Again,
180 #[doc(hidden)]
181 Tls,
182 #[doc(hidden)]
183 Reserved(u16),
184 #[doc(hidden)]
185 Iana(u16),
186 #[doc(hidden)]
187 Library(u16),
188 #[doc(hidden)]
189 Bad(u16),
190}
191
192impl CloseCode {
193 #[must_use]
195 pub fn is_allowed(self) -> bool {
196 !matches!(
197 self,
198 Self::Bad(_) | Self::Reserved(_) | Self::Status | Self::Abnormal | Self::Tls
199 )
200 }
201}
202
203impl fmt::Display for CloseCode {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 let code: u16 = self.into();
206 write!(f, "{code}")
207 }
208}
209
210impl From<CloseCode> for u16 {
211 fn from(code: CloseCode) -> Self {
212 match code {
213 CloseCode::Normal => 1000,
214 CloseCode::Away => 1001,
215 CloseCode::Protocol => 1002,
216 CloseCode::Unsupported => 1003,
217 CloseCode::Status => 1005,
218 CloseCode::Abnormal => 1006,
219 CloseCode::Invalid => 1007,
220 CloseCode::Policy => 1008,
221 CloseCode::Size => 1009,
222 CloseCode::Extension => 1010,
223 CloseCode::Error => 1011,
224 CloseCode::Restart => 1012,
225 CloseCode::Again => 1013,
226 CloseCode::Tls => 1015,
227 CloseCode::Reserved(code)
228 | CloseCode::Iana(code)
229 | CloseCode::Library(code)
230 | CloseCode::Bad(code) => code,
231 }
232 }
233}
234
235impl<'t> From<&'t CloseCode> for u16 {
236 fn from(code: &'t CloseCode) -> Self {
237 (*code).into()
238 }
239}
240
241impl From<u16> for CloseCode {
242 fn from(code: u16) -> Self {
243 match code {
244 1000 => Self::Normal,
245 1001 => Self::Away,
246 1002 => Self::Protocol,
247 1003 => Self::Unsupported,
248 1005 => Self::Status,
249 1006 => Self::Abnormal,
250 1007 => Self::Invalid,
251 1008 => Self::Policy,
252 1009 => Self::Size,
253 1010 => Self::Extension,
254 1011 => Self::Error,
255 1012 => Self::Restart,
256 1013 => Self::Again,
257 1015 => Self::Tls,
258 1016..=2999 => Self::Reserved(code),
259 3000..=3999 => Self::Iana(code),
260 4000..=4999 => Self::Library(code),
261 _ => Self::Bad(code),
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn opcode_from_u8() {
272 let byte = 2u8;
273 assert_eq!(
274 OpCode::try_from(byte).unwrap(),
275 OpCode::Data(OpCodeData::Binary)
276 );
277 }
278
279 #[test]
280 fn opcode_into_u8() {
281 let text = OpCode::Data(OpCodeData::Text);
282 let byte: u8 = text.into();
283 assert_eq!(byte, 1u8);
284 }
285
286 #[test]
287 fn closecode_from_u16() {
288 let byte = 1008u16;
289 assert_eq!(CloseCode::from(byte), CloseCode::Policy);
290 }
291
292 #[test]
293 fn closecode_into_u16() {
294 let text = CloseCode::Away;
295 let byte: u16 = text.into();
296 assert_eq!(byte, 1001u16);
297 assert_eq!(u16::from(text), 1001u16);
298 }
299}