1use crate::util::msg_id;
2
3#[derive(PartialEq, Eq, Clone, Copy, Debug)]
5pub enum Operation {
6 SendVariable, RequireVariable, Invoke, GetVersion, Start, EndTransaction, Acknowledge, Query, Return, Empty, Data, Await, Error, }
20
21impl Operation {
22 pub fn to_name(&self) -> &'static str {
24 match self {
25 Operation::SendVariable => "SENDV",
26 Operation::RequireVariable => "REQUV",
27 Operation::Invoke => "INVOK",
28 Operation::GetVersion => "PKVER",
29 Operation::Start => "START",
30 Operation::EndTransaction => "ENDTR",
31 Operation::Acknowledge => "ACKNO",
32 Operation::Query => "QUERY",
33 Operation::Return => "RTURN",
34 Operation::Empty => "EMPTY",
35 Operation::Data => "SDATA",
36 Operation::Await => "AWAIT",
37 Operation::Error => "ERROR",
38 }
39 }
40
41 pub fn from_name(name: &str) -> Option<Operation> {
43 match name {
44 "SENDV" => Some(Operation::SendVariable),
45 "REQUV" => Some(Operation::RequireVariable),
46 "INVOK" => Some(Operation::Invoke),
47 "PKVER" => Some(Operation::GetVersion),
48 "START" => Some(Operation::Start),
49 "ENDTR" => Some(Operation::EndTransaction),
50 "ACKNO" => Some(Operation::Acknowledge),
51 "QUERY" => Some(Operation::Query),
52 "RTURN" => Some(Operation::Return),
53 "EMPTY" => Some(Operation::Empty),
54 "SDATA" => Some(Operation::Data),
55 "AWAIT" => Some(Operation::Await),
56 "ERROR" => Some(Operation::Error),
57 _ => None,
58 }
59 }
60
61 pub fn is_root(&self) -> bool {
63 match self {
64 Operation::SendVariable
65 | Operation::RequireVariable
66 | Operation::Invoke
67 | Operation::GetVersion => true,
68 _ => false,
69 }
70 }
71}
72
73#[derive(PartialEq, Eq, Clone, Copy)]
75pub enum Role {
76 Host, Device, Idle, }
83
84#[derive(PartialEq, Clone, Debug)]
86pub struct Command {
87 pub msg_id: u16,
88 pub operation: Operation,
89 pub object: Option<String>,
90 pub data: Option<Vec<u8>>,
91}
92
93impl Command {
94 pub fn parse(msg_bytes: &[u8]) -> Result<Command, &'static str> {
104 if msg_bytes.len() < 7 {
106 return Err("Invalid length: message is too short.");
107 }
108
109 let msg_id_slice = msg_bytes.get(0..2).ok_or("Failed to slice MSG ID")?;
111
112 if msg_id_slice == b" " {
114 let op_name_slice = msg_bytes.get(2..7);
116 let space1_slice = msg_bytes.get(7..8);
117 let object_slice = msg_bytes.get(8..13);
118
119 let is_ackno_error = op_name_slice == Some(b"ACKNO")
120 && space1_slice == Some(b" ")
121 && object_slice == Some(b"ERROR");
122 let is_error_error = op_name_slice == Some(b"ERROR")
123 && space1_slice == Some(b" ")
124 && object_slice == Some(b"ERROR");
125
126 if !(is_ackno_error || is_error_error) {
127 return Err("Invalid ERROR command format.");
128 }
129
130 let data = if msg_bytes.len() > 14 {
131 if msg_bytes.get(13..14) != Some(b" ") {
133 return Err("Missing space before data in ERROR command.");
134 }
135 Some(msg_bytes.get(14..).unwrap().to_vec())
137 } else if msg_bytes.len() == 13 {
138 None
140 } else {
141 return Err("Invalid length for ERROR command.");
142 };
143
144 return Ok(Command {
145 msg_id: 0,
146 operation: if op_name_slice == Some(b"ACKNO") {
147 Operation::Acknowledge
148 } else {
149 Operation::Error
150 },
151 object: Some("ERROR".to_string()),
152 data,
153 });
154 }
155
156 let msg_id_str =
158 std::str::from_utf8(msg_id_slice).map_err(|_| "MSG ID is not valid UTF-8")?;
159 let msg_id = msg_id::to_u16(msg_id_str).map_err(|_| "Invalid MSG ID format.")?;
160
161 let op_name_slice = msg_bytes
162 .get(2..7)
163 .ok_or("Failed to slice operation name.")?;
164 let op_name_str =
165 std::str::from_utf8(op_name_slice).map_err(|_| "Operation name is not valid UTF-8")?;
166 let operation = Operation::from_name(op_name_str).ok_or("Unrecognized operation name.")?;
167
168 let (object, data) = match msg_bytes.len() {
170 7 => (None, None),
172
173 13 => {
175 if msg_bytes.get(7..8) != Some(b" ") {
176 return Err("Missing space after operation name.");
177 }
178 let obj_slice = msg_bytes.get(8..13).ok_or("Failed to slice object.")?;
179 let obj_str =
180 std::str::from_utf8(obj_slice).map_err(|_| "Object is not valid UTF-8")?;
181 (Some(obj_str.to_string()), None)
182 }
183
184 len if len > 14 => {
186 if msg_bytes.get(7..8) != Some(b" ") || msg_bytes.get(13..14) != Some(b" ") {
187 return Err("Missing space separator for object or data.");
188 }
189 let obj_slice = msg_bytes.get(8..13).ok_or("Failed to slice object.")?;
190 let obj_str =
191 std::str::from_utf8(obj_slice).map_err(|_| "Object is not valid UTF-8")?;
192
193 let data_slice = msg_bytes.get(14..).unwrap();
195 (Some(obj_str.to_string()), Some(data_slice.to_vec()))
196 }
197 _ => return Err("Invalid message length."),
199 };
200
201 Ok(Command {
202 msg_id,
203 operation,
204 object,
205 data,
206 })
207 }
208
209 pub fn to_bytes(&self) -> Vec<u8> {
214 let id = match self.operation {
215 Operation::Error => String::from(" "),
216 Operation::Acknowledge => {
218 if self.object == Some(String::from("ERROR")) {
219 String::from(" ")
220 } else {
221 msg_id::from_u16(self.msg_id)
222 .map_err(|_| panic!("Invalid MSG ID"))
223 .unwrap()
224 }
225 }
226 _ => msg_id::from_u16(self.msg_id)
227 .map_err(|_| panic!("Invalid MSG ID"))
228 .unwrap(),
229 };
230 if self.data.is_none() {
231 if self.object.is_none() {
232 format!("{}{}", id, self.operation.to_name())
233 .as_bytes()
234 .to_vec()
235 } else {
236 format!(
237 "{}{} {}",
238 id,
239 self.operation.to_name(),
240 self.object.clone().unwrap()
241 )
242 .as_bytes()
243 .to_vec()
244 }
245 } else {
246 let mut vec = format!(
247 "{}{} {}",
248 id,
249 self.operation.to_name(),
250 self.object.clone().unwrap()
251 )
252 .as_bytes()
253 .to_vec();
254 vec.push(b' ');
255 vec.append(&mut self.data.clone().unwrap());
256 vec
257 }
258 }
259}
260
261impl std::fmt::Display for Command {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 let id = match self.operation {
264 Operation::Error => String::from(" "),
265 Operation::Acknowledge => {
267 if self.object == Some(String::from("ERROR")) {
268 String::from(" ")
269 } else {
270 msg_id::from_u16(self.msg_id).map_err(|_| std::fmt::Error)?
271 }
272 }
273 _ => msg_id::from_u16(self.msg_id).map_err(|_| std::fmt::Error)?, };
275
276 let op = self.operation.to_name();
277
278 write!(f, "{}{}", id, op)?;
279
280 if let Some(obj) = &self.object {
281 write!(f, " {}", obj)?;
282 if let Some(data_vec) = &self.data {
283 let data_to_display = if self.operation == Operation::Error
287 || !data_vec.iter().any(|&b| b == 0 || b > 127)
288 {
289 String::from_utf8_lossy(data_vec)
290 } else {
291 String::from_utf8_lossy(data_vec) };
293 write!(f, " {}", data_to_display)?;
294 }
295 }
296
297 Ok(())
298 }
299}
300
301#[derive(PartialEq, Eq, Clone, Copy)]
304pub enum Status {
305 Other, AwaitingAck, AwaitingErrAck, }
312
313#[derive(PartialEq, Eq, Clone, Copy)]
316pub enum Stage {
317 Idle,
318 Started, RootOperationAssigned, SendingParameter, ParameterSent, SendingResponse, }
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::util::msg_id;
329
330 #[test]
331 fn test_command_parse_valid_simple() {
332 let bytes = b"!!START";
333 let cmd = Command::parse(bytes).unwrap();
334 assert_eq!(cmd.msg_id, 0);
335 assert_eq!(cmd.operation, Operation::Start);
336 assert!(cmd.object.is_none());
337 assert!(cmd.data.is_none());
338 }
339
340 #[test]
341 fn test_command_parse_valid_with_object() {
342 let bytes = b"!\"SENDV VARIA";
343 let cmd = Command::parse(bytes).unwrap();
344 assert_eq!(cmd.msg_id, msg_id::to_u16("!\"").unwrap());
345 assert_eq!(cmd.operation, Operation::SendVariable);
346 assert_eq!(cmd.object, Some("VARIA".to_string()));
347 assert!(cmd.data.is_none());
348 }
349
350 #[test]
351 fn test_command_parse_valid_with_object_and_data() {
352 let bytes = b"!#SENDV VARIA data_payload";
353 let cmd = Command::parse(bytes).unwrap();
354 assert_eq!(cmd.msg_id, msg_id::to_u16("!#").unwrap());
355 assert_eq!(cmd.operation, Operation::SendVariable);
356 assert_eq!(cmd.object, Some("VARIA".to_string()));
357 assert_eq!(cmd.data, Some(b"data_payload".to_vec()));
358 }
359
360 #[test]
361 fn test_command_parse_error_command() {
362 let bytes = b" ERROR ERROR Some error description";
363 let cmd = Command::parse(bytes).unwrap();
364 assert_eq!(cmd.msg_id, 0);
365 assert_eq!(cmd.operation, Operation::Error);
366 assert_eq!(cmd.object, Some("ERROR".to_string()));
367 assert_eq!(cmd.data, Some(b"Some error description".to_vec()));
368 }
369
370 #[test]
371 fn test_command_parse_ackno_error_command() {
372 let bytes = b" ACKNO ERROR";
373 let cmd = Command::parse(bytes).unwrap();
374 assert_eq!(cmd.msg_id, 0);
375 assert_eq!(cmd.operation, Operation::Acknowledge);
376 assert_eq!(cmd.object, Some("ERROR".to_string()));
377 assert!(cmd.data.is_none());
378 }
379
380 #[test]
381 fn test_command_parse_invalid_error_msg_id() {
382 let byetes = b" START";
384 let result = Command::parse(byetes);
385 assert!(result.is_err());
386 }
387
388 #[test]
389 fn test_command_parse_invalid_too_short() {
390 let result = Command::parse(b"!!STA");
395 assert!(result.is_err());
396 assert_eq!(result.unwrap_err(), "Invalid length: message is too short.");
397 }
398
399 #[test]
400 fn test_command_parse_invalid_msg_id() {
401 let result = Command::parse(b"\n\rSTART");
403 assert!(result.is_err());
404 assert_eq!(result.unwrap_err(), "Invalid MSG ID format.");
405 }
406
407 #[test]
408 fn test_command_to_bytes_simple() {
409 let cmd = Command {
410 msg_id: 0,
411 operation: Operation::Start,
412 object: None,
413 data: None,
414 };
415 assert_eq!(cmd.to_bytes(), b"!!START".to_vec());
416 }
417
418 #[test]
419 fn test_command_to_bytes_with_object_and_data() {
420 let cmd = Command {
421 msg_id: msg_id::to_u16("!#").unwrap(),
422 operation: Operation::SendVariable,
423 object: Some("VARIA".to_string()),
424 data: Some(b"payload".to_vec()),
425 };
426
427 let mut expected = b"!#SENDV VARIA".to_vec();
428 expected.push(b' ');
429 expected.extend_from_slice(b"payload");
430 assert_eq!(cmd.to_bytes(), expected);
431 }
432
433 #[test]
434 fn test_command_to_bytes_error() {
435 let cmd = Command {
436 msg_id: 0, operation: Operation::Error,
438 object: Some("ERROR".to_string()),
439 data: Some(b"Test error".to_vec()),
440 };
441 let mut expected = b" ERROR ERROR".to_vec();
442 expected.push(b' ');
443 expected.extend_from_slice(b"Test error");
444 assert_eq!(cmd.to_bytes(), expected);
445 }
446}