#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecordHeader {
Definition {
local_mesg_num: u8,
has_dev_data: bool,
},
Data {
local_mesg_num: u8,
},
CompressedTimestamp {
local_mesg_num: u8,
timestamp_offset: u8,
},
}
impl RecordHeader {
pub const COMPRESSED_TIMESTAMP_MASK: u8 = 0x80;
pub const DEFINITION_MASK: u8 = 0x40;
pub const DEV_DATA_MASK: u8 = 0x20;
pub const LOCAL_MESG_NUM_MASK: u8 = 0x0F;
pub const COMPRESSED_LOCAL_MASK: u8 = 0x60;
pub const TIMESTAMP_OFFSET_MASK: u8 = 0x1F;
pub fn classify(byte: u8) -> Self {
if byte & Self::COMPRESSED_TIMESTAMP_MASK != 0 {
Self::CompressedTimestamp {
local_mesg_num: (byte & Self::COMPRESSED_LOCAL_MASK) >> 5,
timestamp_offset: byte & Self::TIMESTAMP_OFFSET_MASK,
}
} else if byte & Self::DEFINITION_MASK != 0 {
Self::Definition {
local_mesg_num: byte & Self::LOCAL_MESG_NUM_MASK,
has_dev_data: byte & Self::DEV_DATA_MASK != 0,
}
} else {
Self::Data {
local_mesg_num: byte & Self::LOCAL_MESG_NUM_MASK,
}
}
}
pub fn local_mesg_num(&self) -> u8 {
match self {
Self::Definition { local_mesg_num, .. }
| Self::Data { local_mesg_num }
| Self::CompressedTimestamp { local_mesg_num, .. } => *local_mesg_num,
}
}
pub fn has_dev_data(&self) -> bool {
matches!(
self,
Self::Definition {
has_dev_data: true,
..
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classifies_data_record() {
let h = RecordHeader::classify(0x00);
assert_eq!(h, RecordHeader::Data { local_mesg_num: 0 });
let h = RecordHeader::classify(0x05);
assert_eq!(h, RecordHeader::Data { local_mesg_num: 5 });
let h = RecordHeader::classify(0x0F);
assert_eq!(h, RecordHeader::Data { local_mesg_num: 15 });
}
#[test]
fn classifies_definition_record() {
let h = RecordHeader::classify(0x40);
assert_eq!(
h,
RecordHeader::Definition {
local_mesg_num: 0,
has_dev_data: false
}
);
let h = RecordHeader::classify(0x43);
assert_eq!(
h,
RecordHeader::Definition {
local_mesg_num: 3,
has_dev_data: false
}
);
}
#[test]
fn classifies_definition_with_dev_data() {
let h = RecordHeader::classify(0x60);
assert_eq!(
h,
RecordHeader::Definition {
local_mesg_num: 0,
has_dev_data: true
}
);
assert!(h.has_dev_data());
let h = RecordHeader::classify(0x6A);
assert_eq!(
h,
RecordHeader::Definition {
local_mesg_num: 10,
has_dev_data: true
}
);
}
#[test]
fn classifies_compressed_timestamp() {
let h = RecordHeader::classify(0x80);
assert_eq!(
h,
RecordHeader::CompressedTimestamp {
local_mesg_num: 0,
timestamp_offset: 0
}
);
let h = RecordHeader::classify(0xEA);
assert_eq!(
h,
RecordHeader::CompressedTimestamp {
local_mesg_num: 3,
timestamp_offset: 10
}
);
let h = RecordHeader::classify(0xBF);
assert_eq!(
h,
RecordHeader::CompressedTimestamp {
local_mesg_num: 1,
timestamp_offset: 31
}
);
}
#[test]
fn local_mesg_num_accessor() {
assert_eq!(
RecordHeader::Definition {
local_mesg_num: 7,
has_dev_data: false
}
.local_mesg_num(),
7
);
assert_eq!(
RecordHeader::Data { local_mesg_num: 12 }.local_mesg_num(),
12
);
assert_eq!(
RecordHeader::CompressedTimestamp {
local_mesg_num: 2,
timestamp_offset: 5
}
.local_mesg_num(),
2
);
}
#[test]
fn dev_data_only_set_for_definition() {
assert!(!RecordHeader::Data { local_mesg_num: 0 }.has_dev_data());
assert!(!RecordHeader::CompressedTimestamp {
local_mesg_num: 0,
timestamp_offset: 0
}
.has_dev_data());
}
}