eldritchwire/
lib.rs

1pub mod commands;
2mod error;
3use commands::Command;
4use error::EldritchError;
5use std::fmt::Debug;
6
7#[derive(Clone, Debug, PartialEq)]
8pub struct AddressedCommand {
9    pub device_id: u8,
10    pub command: Command,
11}
12
13#[derive(Clone, PartialEq, PartialOrd)]
14pub struct FixedPointDecimal {
15    raw_val: i16,
16}
17
18impl FixedPointDecimal {
19    pub fn get_real_val(&self) -> f32 {
20        f32::from(self.raw_val) / 2_f32.powi(11)
21    }
22
23    pub fn get_rounded_val(&self) -> f32 {
24        (self.get_real_val() * 100.0).round() / 100.0
25    }
26
27    pub fn from_data(data: &[u8; 2]) -> Self {
28        assert!(data.len() == 2);
29        Self {
30            raw_val: u16::from_le_bytes(*data) as i16,
31        }
32    }
33}
34
35impl Debug for FixedPointDecimal {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(
38            f,
39            "FixedPointDecimal {{ raw_val: {}, real_val: {} }}",
40            self.raw_val,
41            self.get_real_val()
42        )
43    }
44}
45
46impl PartialEq<f32> for FixedPointDecimal {
47    fn eq(&self, other: &f32) -> bool {
48        &self.get_real_val() == other
49    }
50}
51
52impl PartialOrd<f32> for FixedPointDecimal {
53    fn partial_cmp(&self, other: &f32) -> Option<std::cmp::Ordering> {
54        let real_val = self.get_real_val();
55        if &real_val == other {
56            Some(std::cmp::Ordering::Equal)
57        } else if &real_val < other {
58            Some(std::cmp::Ordering::Less)
59        } else {
60            Some(std::cmp::Ordering::Greater)
61        }
62    }
63}
64
65#[derive(Clone, Debug, PartialEq)]
66pub enum Operation {
67    Assign,
68    Increment,
69    Toggle,
70}
71
72#[derive(Clone, Debug, PartialEq)]
73struct CommandHeader {
74    device_id: u8,
75    command_length: u8,
76    command_id: u8,
77}
78
79#[derive(Debug, PartialEq)]
80struct PacketData {
81    data: Vec<u8>,
82    cursor: u8,
83}
84
85impl PacketData {
86    pub fn new(packet_data: Vec<u8>) -> Result<Self, EldritchError> {
87        if packet_data.len() > 255 {
88            Err(EldritchError::PacketToLarge)
89        } else {
90            Ok(Self {
91                data: packet_data,
92                cursor: 0,
93            })
94        }
95    }
96
97    pub fn parse_header(&mut self) -> Result<CommandHeader, EldritchError> {
98        let device_id = self
99            .data
100            .get(self.cursor as usize)
101            .ok_or(EldritchError::InvalidHeader)?;
102        let command_length = self
103            .data
104            .get((self.cursor + 1) as usize)
105            .ok_or(EldritchError::InvalidHeader)?;
106        let command_id = self
107            .data
108            .get((self.cursor + 2) as usize)
109            .ok_or(EldritchError::InvalidHeader)?;
110
111        let header = if let Some(reserved) = self.data.get((self.cursor + 3) as usize) {
112            if *reserved == 0 {
113                Ok(CommandHeader {
114                    device_id: *device_id,
115                    command_length: *command_length,
116                    command_id: *command_id,
117                })
118            } else {
119                Err(EldritchError::InvalidHeader)
120            }
121        } else {
122            Err(EldritchError::InvalidHeader)
123        };
124        self.cursor += 4;
125        header
126    }
127
128    fn has_data(&self) -> bool {
129        usize::from(self.cursor) < self.data.len()
130    }
131
132    fn get_slice(&mut self, slice_len: u8) -> Result<&[u8], EldritchError> {
133        let new_cur = self.cursor + slice_len;
134        if usize::from(new_cur) > self.data.len() {
135            return Err(EldritchError::EndOfPacket);
136        }
137        let slice_data = &self.data[usize::from(self.cursor)..usize::from(new_cur)];
138        self.cursor = new_cur;
139        Ok(slice_data)
140    }
141}
142
143pub fn parse_frame_packet(data: Vec<u8>) -> Result<Vec<AddressedCommand>, EldritchError> {
144    let mut packet = PacketData::new(data)?;
145    let mut commands: Vec<AddressedCommand> = Vec::new();
146
147    while packet.has_data() {
148        let header = packet.parse_header()?;
149        // println!("Command Header: {header:?}");
150        match packet.get_slice(header.command_length) {
151            Ok(command_data) => {
152                // println!("Command Data: {command_data:?}");
153                commands.push(AddressedCommand {
154                    device_id: header.device_id,
155                    command: commands::parse_command(command_data)?,
156                });
157            }
158            Err(EldritchError::EndOfPacket) => break,
159            Err(err) => Err(err)?,
160        };
161
162        match packet.get_slice(calculate_padding_length(header.command_length)) {
163            Ok(padding) => verify_padding(padding, header.command_length)?,
164            Err(EldritchError::EndOfPacket) => break,
165            Err(err) => Err(err)?,
166        };
167    }
168
169    Ok(commands)
170}
171
172fn calculate_padding_length(command_length: u8) -> u8 {
173    if command_length.is_multiple_of(4) {
174        0
175    } else {
176        4 - (command_length % 4)
177    }
178}
179
180fn verify_padding(padding: &[u8], command_length: u8) -> Result<(), EldritchError> {
181    if !(padding.len() + usize::from(command_length)).is_multiple_of(4) {
182        return Err(EldritchError::PaddingViolation(String::from(
183            "Padding length is incorrect",
184        )));
185    }
186
187    let mut padding_errors: Vec<Result<(), EldritchError>> = padding
188        .iter()
189        .enumerate()
190        .map(|(idx, byte)| {
191            if *byte != 0x00 {
192                Err(EldritchError::PaddingViolation(format!(
193                    "Padding byte at index: {idx} is not 0x00",
194                )))
195            } else {
196                Ok(())
197            }
198        })
199        .filter(|check| check.is_err())
200        .collect();
201
202    if !padding_errors.is_empty() {
203        padding_errors.remove(0)
204    } else {
205        Ok(())
206    }
207}
208
209#[cfg(test)]
210mod fixed_point_test {
211    use super::*;
212
213    #[test]
214    fn fpd_fifteen_percent() {
215        let fifteen_percent = FixedPointDecimal { raw_val: 0x0133 };
216        let float_value = fifteen_percent.get_rounded_val();
217        assert_eq!(float_value, 0.15);
218    }
219
220    #[test]
221    fn fpd_minus_point_three() {
222        let minus_point_three = FixedPointDecimal {
223            raw_val: 0xfd9au16 as i16,
224        };
225        let float_value = minus_point_three.get_rounded_val();
226        assert_eq!(float_value, -0.3_f32);
227    }
228}
229
230#[cfg(test)]
231mod packet_data_test {
232    use crate::commands::{
233        color_correction_commands::{ColorCorrectionCommand, RedGreenBlueLuma},
234        display_commands::DisplayCommand,
235        lens_commands::LensCommand,
236        video_commands::{VideoCommand, VideoModeData},
237    };
238
239    use super::*;
240
241    #[test]
242    fn new() {
243        let packet_data = vec![
244            0x00, 0x05, 0x00, 0x00, // Header
245            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
246            0x00, 0x00, 0x00, // Padding
247        ];
248
249        if let Ok(packet) = PacketData::new(packet_data) {
250            assert_eq!(packet.cursor, 0);
251            assert_eq!(packet.data.len(), 12);
252        } else {
253            panic!();
254        }
255    }
256
257    #[test]
258    fn parse_header() {
259        let packet_data = vec![
260            0x00, 0x05, 0x00, 0x00, // Header
261            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
262            0x00, 0x00, 0x00, // Padding
263        ];
264
265        let mut packet =
266            PacketData::new(packet_data).expect("Failed to build PacketData with known good data");
267
268        if let Ok(header) = packet.parse_header() {
269            assert_eq!(
270                header,
271                CommandHeader {
272                    device_id: 0,
273                    command_length: 5,
274                    command_id: 0
275                }
276            );
277        } else {
278            panic!();
279        }
280    }
281
282    #[test]
283    fn parse_header_bad_reserved_byte() {
284        let packet_data = vec![
285            0x00, 0x05, 0x00, 0xFF, // Header
286            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
287            0x00, 0x00, 0x00, // Padding
288        ];
289
290        let mut packet = PacketData::new(packet_data).expect("Verified packet data");
291
292        if let Err(error) = packet.parse_header() {
293            assert_eq!(error, EldritchError::InvalidHeader);
294            assert_eq!(4, packet.cursor);
295        } else {
296            panic!();
297        }
298    }
299
300    #[test]
301    fn has_data_true() {
302        let packet_data = vec![
303            0x00, 0x05, 0x00, 0xFF, // Header
304            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
305            0x00, 0x00, 0x00, // Padding
306        ];
307
308        let mut packet = PacketData::new(packet_data).expect("Verified packet data");
309        packet.cursor = 10;
310        println!("Packet: {:?}", packet);
311        assert!(packet.has_data());
312    }
313
314    #[test]
315    fn has_data_false() {
316        let packet_data = vec![
317            0x00, 0x05, 0x00, 0xFF, // Header
318            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
319            0x00, 0x00, 0x00, // Padding
320        ];
321
322        let mut packet = PacketData::new(packet_data).expect("Verified packet data");
323        packet.cursor = 12;
324        println!("Packet: {:?}", packet);
325        assert!(!packet.has_data());
326    }
327
328    #[test]
329    fn get_slice() {
330        let packet_data = vec![
331            0x00, 0x05, 0x00, 0xFF, // Header
332            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
333            0x00, 0x00, 0x00, // Padding
334        ];
335        let mut packet = PacketData::new(packet_data).expect("Verified packet data");
336        packet.cursor = 4;
337
338        if let Ok(cmd_data) = packet.get_slice(5) {
339            assert_eq!([0x00, 0x80, 0x01, 0x9a, 0xfd,] as [u8; 5], cmd_data);
340            assert_eq!(9, packet.cursor);
341        } else {
342            panic!();
343        }
344        if let Ok(padding) = packet.get_slice(calculate_padding_length(5)) {
345            assert_eq!([0x00, 0x00, 0x00] as [u8; 3], padding);
346            assert_eq!(12, packet.cursor);
347        } else {
348            panic!();
349        }
350    }
351
352    #[test]
353    fn get_slice_error() {
354        let packet_data = vec![
355            0x00, 0x05, 0x00, 0xFF, // Header
356            0x00, 0x80, 0x01, 0x9a, 0xfd, // Command
357            0x00, 0x00, 0x00, // Padding
358        ];
359        let mut packet = PacketData::new(packet_data).expect("Verified packet data");
360        packet.cursor = 8;
361
362        if let Err(error) = packet.get_slice(5) {
363            assert_eq!(EldritchError::EndOfPacket, error);
364        } else {
365            panic!();
366        }
367    }
368
369    #[test]
370    fn parse_large_packet() {
371        let frame_packet = vec![
372            0x04, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x05, 0x00, 0x00, 0x00, 0x06,
373            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x01, 0x05, 0x03, 0x00,
374            0x10, 0x27, 0x00, 0x00, 0x04, 0x06, 0x00, 0x00, 0x04, 0x02, 0x80, 0x01, 0x33, 0x01,
375            0x00, 0x00, 0xff, 0x09, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x18, 0x01, 0x03, 0x00,
376            0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x00, 0x08, 0x01, 0x80, 0x01, 0x00, 0x00,
377            0x9a, 0xfd, 0x9a, 0xfd, 0x00, 0x00,
378        ];
379        let commands = parse_frame_packet(frame_packet).expect("Test Frame should parse");
380        let mut commands = commands.iter();
381
382        assert_eq!(
383            commands.next(),
384            Some(AddressedCommand {
385                device_id: 4,
386                command: Command::Lens(LensCommand::InstantaneousAutoFocus),
387            })
388            .as_ref()
389        );
390
391        assert_eq!(
392            commands.next(),
393            Some(AddressedCommand {
394                device_id: 255,
395                command: Command::Lens(LensCommand::OpticalImageStabalization {
396                    operation: Operation::Assign,
397                    data: true
398                })
399            })
400            .as_ref()
401        );
402
403        assert_eq!(
404            commands.next(),
405            Some(AddressedCommand {
406                device_id: 4,
407                command: Command::Video(VideoCommand::ExposureUS {
408                    operation: Operation::Assign,
409                    data: 10000
410                })
411            })
412            .as_ref()
413        );
414
415        assert_eq!(
416            commands.next(),
417            Some(AddressedCommand {
418                device_id: 4,
419                command: Command::Display(DisplayCommand::ZebraLevel {
420                    operation: Operation::Increment,
421                    data: FixedPointDecimal {
422                        raw_val: 0x0133u16 as i16
423                    }
424                })
425            })
426            .as_ref()
427        );
428
429        assert_eq!(
430            commands.next(),
431            Some(AddressedCommand {
432                device_id: 255,
433                command: Command::Video(VideoCommand::VideoMode {
434                    operation: Operation::Assign,
435                    data: VideoModeData {
436                        frame_rate: 24,
437                        m_rate: 1,
438                        dimensions: 3,
439                        interlaced: 0,
440                        color_space: 0,
441                    }
442                })
443            })
444            .as_ref()
445        );
446
447        assert_eq!(
448            commands.next(),
449            Some(AddressedCommand {
450                device_id: 4,
451                command: Command::ColorCorrection(ColorCorrectionCommand::GammaAdjust {
452                    operation: Operation::Increment,
453                    data: RedGreenBlueLuma {
454                        red: FixedPointDecimal { raw_val: 0x00 },
455                        green: FixedPointDecimal {
456                            raw_val: 0xfd9au16 as i16
457                        },
458                        blue: FixedPointDecimal {
459                            raw_val: 0xfd9au16 as i16
460                        },
461                        luma: FixedPointDecimal { raw_val: 0x00 },
462                    }
463                })
464            })
465            .as_ref()
466        );
467    }
468
469    #[test]
470    fn long_real_packet_one() {
471        let cmd_data: [u8; 243] = [
472            0x01, 0x06, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x8D, 0x1C, 0x00, 0x00, 0x01, 0x06,
473            0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00,
474            0x01, 0x0D, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x01,
475            0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00,
476            0x04, 0x10, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x05, 0x03, 0x00, 0x1B, 0x41,
477            0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
478            0x01, 0x05, 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C,
479            0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00,
480            0x01, 0x0C, 0x00, 0x00, 0x08, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481            0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x08, 0x02, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08,
482            0x00, 0x08, 0xCE, 0x07, 0x01, 0x0C, 0x00, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x00,
483            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x04, 0x80, 0x00,
484            0x00, 0x04, 0x00, 0x08, 0x01, 0x06, 0x00, 0x00, 0x08, 0x05, 0x80, 0x00, 0x00, 0x08,
485            0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08,
486            0x02, 0x06, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x9B, 0x12, 0x00, 0x00, 0x02, 0x06,
487            0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00,
488            0x01, 0x0D, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x01,
489            0x01, 0x00, 0x08, 0x00, 0x00,
490        ];
491        let result = parse_frame_packet(cmd_data.to_vec());
492        let _commands = result.unwrap();
493    }
494
495    #[test]
496    fn long_real_packet_two() {
497        let cmd_data: [u8; 251] = [
498            0x03, 0x0C, 0x00, 0x00, 0x08, 0x02, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08,
499            0x00, 0x08, 0x03, 0x0C, 0x00, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
500            0x00, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x08, 0x04, 0x80, 0x00, 0x00, 0x04,
501            0x00, 0x08, 0x03, 0x06, 0x00, 0x00, 0x08, 0x05, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00,
502            0x03, 0x08, 0x00, 0x00, 0x08, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x06,
503            0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0xF2, 0x15, 0x00, 0x00, 0x04, 0x06, 0x00, 0x00,
504            0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x01, 0x0D,
505            0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00,
506            0x02, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x04, 0x10,
507            0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x01, 0x05, 0x03, 0x00, 0x1B, 0x41, 0x00, 0x00,
508            0x04, 0x05, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x05,
509            0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00,
510            0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C,
511            0x00, 0x00, 0x08, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
512            0x04, 0x0C, 0x00, 0x00, 0x08, 0x02, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08,
513            0x00, 0x08, 0x04, 0x0C, 0x00, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
514            0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x08, 0x04, 0x80, 0x00, 0x00, 0x04,
515            0x00, 0x08, 0x04, 0x06, 0x00, 0x00, 0x08, 0x05, 0x80, 0x00, 0x00, 0x08, 0x00,
516        ];
517        let result = parse_frame_packet(cmd_data.to_vec());
518        let _commands = result.unwrap();
519    }
520
521    #[test]
522    fn long_real_packet_three() {
523        let cmd_data: [u8; 247] = [
524            0x10, 0x05, 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0C,
525            0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
526            0x10, 0x0C, 0x00, 0x00, 0x08, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
527            0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, 0x08, 0x02, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08,
528            0x00, 0x08, 0x00, 0x08, 0x10, 0x0C, 0x00, 0x00, 0x08, 0x03, 0x80, 0x00, 0x00, 0x00,
529            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x08, 0x04, 0x80, 0x00,
530            0x00, 0x04, 0x00, 0x08, 0x10, 0x06, 0x00, 0x00, 0x08, 0x05, 0x80, 0x00, 0x00, 0x08,
531            0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x08, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08,
532            0x11, 0x06, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x05,
533            0x00, 0x00, 0x01, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x00,
534            0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x01, 0x05,
535            0x03, 0x00, 0x20, 0x4E, 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00,
536            0x01, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00,
537            0x00, 0x00, 0x11, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
538            0x00, 0x00, 0x00, 0x00, 0x11, 0x0C, 0x00, 0x00, 0x08, 0x01, 0x80, 0x00, 0x00, 0x00,
539            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x0C, 0x00, 0x00, 0x08, 0x02, 0x80, 0x00,
540            0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x11, 0x0C, 0x00, 0x00, 0x08, 0x03,
541            0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
542        ];
543        let result = parse_frame_packet(cmd_data.to_vec());
544        let _commands = result.unwrap();
545    }
546
547    #[test]
548    fn short_real_packet_one() {
549        let cmd_data: [u8; 23] = [
550            0x02, 0x06, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x64, 0x20, 0x00, 0x00, 0x02, 0x06,
551            0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x07, 0x21, 0x00,
552        ];
553        let result = parse_frame_packet(cmd_data.to_vec());
554        let _commands = result.unwrap();
555    }
556}
557
558#[cfg(test)]
559mod lib_test {
560    use super::*;
561    use crate::commands::{lens_commands::LensCommand, Command};
562
563    #[test]
564    fn parse_packet_single_command() {
565        let packet_data = vec![
566            0x00, 0x06, 0x00, 0x00, // Header
567            0x00, 0x00, 0x80, 0x01, 0x33, 0x01, // Command
568            0x00, 0x00, // Padding
569        ];
570
571        if let Ok(commands) = parse_frame_packet(packet_data) {
572            println!("parse_packet_single_command() commands: {:?}", commands);
573            assert_eq!(1, commands.len());
574            assert_eq!(
575                AddressedCommand {
576                    device_id: 0,
577                    command: Command::Lens(LensCommand::Focus {
578                        operation: Operation::Increment,
579                        data: FixedPointDecimal {
580                            raw_val: 0x0133u16 as i16
581                        }
582                    })
583                },
584                *commands.first().expect("Length asserted to be one")
585            );
586        } else {
587            panic!();
588        }
589    }
590
591    #[test]
592    fn parse_packet_two_commands() {
593        let packet_data = vec![
594            0x00, 0x06, 0x00, 0x00, // Header
595            0x00, 0x00, 0x80, 0x01, 0x33, 0x01, // Command
596            0x00, 0x00, // Padding
597            0x00, 0x06, 0x00, 0x00, // Header
598            0x00, 0x00, 0x80, 0x01, 0x33, 0x01, // Command
599            0x00, 0x00, // Padding
600        ];
601
602        if let Ok(commands) = parse_frame_packet(packet_data) {
603            println!("parse_packet_single_command() commands: {:?}", commands);
604            assert_eq!(2, commands.len());
605            assert_eq!(
606                AddressedCommand {
607                    device_id: 0,
608                    command: Command::Lens(LensCommand::Focus {
609                        operation: Operation::Increment,
610                        data: FixedPointDecimal {
611                            raw_val: 0x0133u16 as i16
612                        }
613                    })
614                },
615                *commands
616                    .first()
617                    .expect("Length asserted to be more then one")
618            );
619            assert_eq!(
620                AddressedCommand {
621                    device_id: 0,
622                    command: Command::Lens(LensCommand::Focus {
623                        operation: Operation::Increment,
624                        data: FixedPointDecimal {
625                            raw_val: 0x0133u16 as i16
626                        }
627                    })
628                },
629                *commands.get(1).expect("Length asserted to be two")
630            );
631        } else {
632            panic!();
633        }
634    }
635
636    #[test]
637    fn calculate_padding_length_no_padding() {
638        assert_eq!(0_u8, calculate_padding_length(8));
639        assert_eq!(0_u8, calculate_padding_length(4));
640    }
641
642    #[test]
643    fn calculate_padding_length_one() {
644        assert_eq!(1_u8, calculate_padding_length(3));
645        assert_eq!(1_u8, calculate_padding_length(7));
646    }
647
648    #[test]
649    fn calculate_padding_length_two() {
650        assert_eq!(2_u8, calculate_padding_length(2));
651        assert_eq!(2_u8, calculate_padding_length(6));
652    }
653
654    #[test]
655    fn calculate_padding_length_three() {
656        assert_eq!(3_u8, calculate_padding_length(1));
657        assert_eq!(3_u8, calculate_padding_length(5));
658    }
659
660    #[test]
661    fn verify_padding_success() {
662        let padding: [u8; 3] = [0x00; 3];
663        let command_length = 5;
664
665        assert_eq!(Ok(()), verify_padding(&padding, command_length));
666    }
667
668    #[test]
669    fn verify_padding_too_short() {
670        let padding: [u8; 2] = [0x00; 2];
671        let command_length = 5;
672
673        assert_eq!(
674            Err(EldritchError::PaddingViolation(String::from(
675                "Padding length is incorrect"
676            ))),
677            verify_padding(&padding, command_length)
678        );
679    }
680
681    #[test]
682    fn verify_padding_nonzero_byte_idx_0() {
683        let padding: [u8; 3] = [0xff, 0x00, 0x00];
684        let command_length = 5;
685
686        assert_eq!(
687            Err(EldritchError::PaddingViolation(String::from(
688                "Padding byte at index: 0 is not 0x00"
689            ))),
690            verify_padding(&padding, command_length),
691        )
692    }
693
694    #[test]
695    fn verify_padding_nonzero_byte_idx_2() {
696        let padding: [u8; 3] = [0x00, 0x00, 0xff];
697        let command_length = 5;
698
699        assert_eq!(
700            Err(EldritchError::PaddingViolation(String::from(
701                "Padding byte at index: 2 is not 0x00"
702            ))),
703            verify_padding(&padding, command_length),
704        )
705    }
706}