1use std::io::Cursor;
8
9use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10use conclave_room_serialize::{PingCommand, RoomInfoCommand};
11use conclave_types::{ClientNonce, GuiseUserSessionId, SessionId};
12
13use crate::ClientReceiveCommand::LoginResponseType;
14use crate::ServerReceiveCommand::{LoginRequestType, PingCommandType};
15
16#[derive(Debug, PartialEq)]
18pub struct LoginRequest {
19 pub guise_user_session_id: GuiseUserSessionId,
20 pub nonce: ClientNonce,
21}
22
23impl LoginRequest {
24 pub fn to_octets(&self) -> Vec<u8> {
25 let mut writer = vec![];
26
27 writer
28 .write_u64::<BigEndian>(self.guise_user_session_id)
29 .unwrap();
30 writer.write_u64::<BigEndian>(self.nonce).unwrap();
31 writer
32 }
33
34 pub fn from_cursor(reader: &mut Cursor<&[u8]>) -> Self {
35 Self {
36 guise_user_session_id: reader.read_u64::<BigEndian>().unwrap(),
37 nonce: reader.read_u64::<BigEndian>().unwrap(),
38 }
39 }
40}
41
42#[derive(Debug)]
43pub enum ServerReceiveCommand {
44 LoginRequestType(LoginRequest),
45 PingCommandType(PingCommand),
46}
47
48impl ServerReceiveCommand {
49 pub fn to_octets(&self) -> Result<Vec<u8>, String> {
50 let command_type_id = match self {
51 LoginRequestType(_) => LOGIN_REQUEST_TYPE_ID,
52 _ => return Err(format!("unsupported command {:?}", self)),
53 };
54
55 let mut writer = vec![];
56
57 writer
58 .write_u8(command_type_id)
59 .expect("could not write command type id");
60
61 match self {
62 LoginRequestType(login_request) => {
63 writer.extend_from_slice(login_request.to_octets().as_slice())
64 }
65 PingCommandType(ping_command) => {
66 writer.extend_from_slice(ping_command.to_octets().as_slice())
67 }
68 }
70
71 Ok(writer)
72 }
73
74 pub fn from_octets(input: &[u8]) -> Result<ServerReceiveCommand, String> {
75 let reader = Cursor::new(input);
76 ServerReceiveCommand::from_cursor(reader)
77 }
78
79 pub fn from_cursor(mut reader: Cursor<&[u8]>) -> Result<ServerReceiveCommand, String> {
80 let command_type_id = reader.read_u8().unwrap();
81 match command_type_id {
82 LOGIN_REQUEST_TYPE_ID => Ok(LoginRequestType(LoginRequest::from_cursor(&mut reader))),
83 _ => Err(format!("unknown command 0x{:x}", command_type_id)),
84 }
85 }
86}
87
88#[derive(Debug)]
90pub struct LoginResponse {
91 pub nonce: ClientNonce,
92 pub session_id: SessionId,
93}
94
95impl LoginResponse {
96 pub fn to_octets(&self) -> Vec<u8> {
97 let mut writer = vec![];
98
99 writer
100 .write_u64::<BigEndian>(self.nonce)
101 .unwrap();
102 writer.write_u64::<BigEndian>(self.session_id).unwrap();
103 writer
104 }
105
106 pub fn from_cursor(reader: &mut Cursor<&[u8]>) -> Self {
107 Self {
108 nonce: reader.read_u64::<BigEndian>().unwrap(),
109 session_id: reader.read_u64::<BigEndian>().unwrap(),
110 }
111 }
112}
113
114#[derive(Debug)]
115pub enum ClientReceiveCommand {
116 LoginResponseType(LoginResponse),
117 RoomInfoType(RoomInfoCommand),
118}
119
120pub const LOGIN_REQUEST_TYPE_ID: u8 = 0x02;
121pub const LOGIN_RESPONSE_TYPE_ID: u8 = 0x22;
122
123impl ClientReceiveCommand {
124 pub fn to_octets(&self) -> Result<Vec<u8>, String> {
125 let command_type_id = match self {
126 LoginResponseType(_) => LOGIN_RESPONSE_TYPE_ID,
127 _ => return Err(format!("unsupported command {:?}", self)),
128 };
129
130 let mut writer = vec![];
131
132 writer
133 .write_u8(command_type_id)
134 .expect("could not write command type id");
135
136 match self {
137 LoginResponseType(login_response) => {
138 writer.extend_from_slice(login_response.to_octets().as_slice())
139 }
140 _ => return Err(format!("unknown command enum {:?}", self)),
141 }
142
143 Ok(writer)
144 }
145
146 pub fn from_octets(input: &[u8]) -> Result<ClientReceiveCommand, String> {
147 let mut rdr = Cursor::new(input);
148 let command_type_id = rdr.read_u8().unwrap();
149 match command_type_id {
150 LOGIN_RESPONSE_TYPE_ID => Ok(LoginResponseType(LoginResponse::from_cursor(&mut rdr))),
151 _ => Err(format!("unknown command 0x{:x}", command_type_id)),
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use std::io::Cursor;
159
160 use crate::{ClientReceiveCommand, LOGIN_REQUEST_TYPE_ID, LOGIN_RESPONSE_TYPE_ID, LoginRequest, LoginResponseType, ServerReceiveCommand};
161 use crate::ServerReceiveCommand::LoginRequestType;
162
163 #[test]
164 fn check_login_request_serializer() {
165 let login_request = LoginRequest {
166 guise_user_session_id: 4214124,
167 nonce: 19,
168 };
169
170 let encoded = login_request.to_octets();
171 let mut receive_cursor = Cursor::new(encoded.as_slice());
172 let deserialized_ping_command = LoginRequest::from_cursor(&mut receive_cursor);
173
174 println!("before {:?}", &login_request);
175 println!("after {:?}", &deserialized_ping_command);
176 assert_eq!(login_request, deserialized_ping_command);
177 }
178
179 #[test]
180 fn check_server_receive_login_request() {
181 let octets = [
182 LOGIN_REQUEST_TYPE_ID,
183 0x00, 0x00,
185 0x00,
186 0x00,
187 0x00,
188 0x40,
189 0x4d,
190 0x6c,
191 0x00, 0x00,
193 0x00,
194 0x00,
195 0x00,
196 0x00,
197 0x00,
198 0x13,
199 ];
200
201 let message = &ServerReceiveCommand::from_octets(&octets).unwrap();
202
203 match message {
204 LoginRequestType(login_request) => {
205 println!("received {:?}", &login_request);
206 assert_eq!(login_request.guise_user_session_id, 4214124);
207 assert_eq!(login_request.nonce, 19);
208 let octets_after = message.to_octets().unwrap();
209 assert_eq!(octets, octets_after.as_slice());
210 }
211 _ => unreachable!("should be login request command"),
212 }
213 }
214
215 #[test]
216 fn check_client_receive_message() {
217 let octets = [
218 LOGIN_RESPONSE_TYPE_ID,
219 0x00, 0x00,
221 0x00,
222 0x09,
223 0xC6,
224 0x36,
225 0xCD,
226 0x41,
227 0x00, 0x00,
229 0x00,
230 0x00,
231 0x3B,
232 0x4B,
233 0xB9,
234 0x6A,
235 ];
236
237 let message = &ClientReceiveCommand::from_octets(&octets).unwrap();
238
239 match message {
240 LoginResponseType(login_response) => {
241 println!("received {:?}", &login_response);
242 assert_eq!(login_response.nonce, 41980185921);
243 assert_eq!(login_response.session_id, 994818410);
244 let octets_after = message.to_octets().unwrap();
245 assert_eq!(octets, octets_after.as_slice());
246 }
247 _ => unreachable!("should be room info command"),
248 }
249 }
250}