use crate::Error;
pub type Address16 = u16;
pub type Address24 = u32;
pub type Address32 = u32;
pub type Count16 = u16;
pub type Count24 = u32;
#[derive(Debug, Clone)]
pub struct Data<T> {
pub address: T,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub enum Address {
Address16(Address16),
Address24(Address24),
Address32(Address32),
}
impl Address {
pub fn to_le_bytes(self) -> Vec<u8> {
match self {
Address::Address16(addr) => addr.to_le_bytes().to_vec(),
Address::Address24(addr) => addr.to_le_bytes().to_vec(),
Address::Address32(addr) => addr.to_le_bytes().to_vec(),
}
}
pub fn to_be_bytes(self) -> Vec<u8> {
match self {
Address::Address16(addr) => addr.to_be_bytes().to_vec(),
Address::Address24(addr) => addr.to_be_bytes().to_vec(),
Address::Address32(addr) => addr.to_be_bytes().to_vec(),
}
}
}
#[derive(Debug, Clone)]
pub enum Record {
S0(String),
S1(Data<Address16>),
S2(Data<Address24>),
S3(Data<Address32>),
S4,
S5(Count16),
S6(Count24),
S7(Address32),
S8(Address24),
S9(Address16),
}
impl Record {
pub fn parse_from_str(record: &str) -> Result<Self, Error> {
let record = record.trim();
if !record.starts_with('S') || record.len() < 2 {
return Err(Error::DataLengthError);
}
let rec_type = &record[1..2];
let bytes = (2..record.len())
.step_by(2)
.map(|i| u8::from_str_radix(&record[i..i + 2], 16).unwrap_or(0))
.collect::<Vec<_>>();
let sum: u32 = bytes.iter().map(|&b| b as u32).sum();
if (sum & 0xFF) != 0xFF {
return Err(Error::CheckSumError);
}
match rec_type {
"0" => {
let data = &bytes[2..bytes.len() - 1];
let text = String::from_utf8_lossy(data).to_string();
Ok(Record::S0(text))
}
"1" => {
let address = ((bytes[1] as u16) << 8) | (bytes[2] as u16);
let data = bytes[3..bytes.len() - 1].to_vec();
Ok(Record::S1(Data { address, data }))
}
"2" => {
let address =
((bytes[1] as u32) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32);
let data = bytes[4..bytes.len() - 1].to_vec();
Ok(Record::S2(Data { address, data }))
}
"3" => {
let address = ((bytes[1] as u32) << 24)
| ((bytes[2] as u32) << 16)
| ((bytes[3] as u32) << 8)
| (bytes[4] as u32);
let data = bytes[5..bytes.len() - 1].to_vec();
Ok(Record::S3(Data { address, data }))
}
"4" => Ok(Record::S4),
"5" => {
let count = ((bytes[1] as u16) << 8) | (bytes[2] as u16);
Ok(Record::S5(count))
}
"6" => {
let count =
((bytes[1] as u32) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32);
Ok(Record::S6(count))
}
"7" => {
let address = ((bytes[1] as u32) << 24)
| ((bytes[2] as u32) << 16)
| ((bytes[3] as u32) << 8)
| (bytes[4] as u32);
Ok(Record::S7(address))
}
"8" => {
let address =
((bytes[1] as u32) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32);
Ok(Record::S8(address))
}
"9" => {
let address = ((bytes[1] as u16) << 8) | (bytes[2] as u16);
Ok(Record::S9(address))
}
_ => Err(Error::DataLengthError),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_address_to_le_bytes() {
let addr16 = Address::Address16(0x1234);
assert_eq!(addr16.to_le_bytes(), 0x1234u16.to_le_bytes().to_vec());
let addr24 = Address::Address24(0x123456);
assert_eq!(addr24.to_le_bytes(), 0x123456u32.to_le_bytes().to_vec());
let addr32 = Address::Address32(0x12345678);
assert_eq!(addr32.to_le_bytes(), 0x12345678u32.to_le_bytes().to_vec());
}
#[test]
fn test_address_to_be_bytes() {
let addr16 = Address::Address16(0x1234);
assert_eq!(addr16.to_be_bytes(), 0x1234u16.to_be_bytes().to_vec());
let addr24 = Address::Address24(0x123456);
assert_eq!(addr24.to_be_bytes(), 0x123456u32.to_be_bytes().to_vec());
let addr32 = Address::Address32(0x12345678);
assert_eq!(addr32.to_be_bytes(), 0x12345678u32.to_be_bytes().to_vec());
}
#[test]
fn test_parse_s1_record() {
let srec = "S1137AF000A0A0D000000000000000000000000072";
let rec = Record::parse_from_str(srec).unwrap();
match rec {
Record::S1(data) => {
assert_eq!(data.address, 0x7AF0);
assert_eq!(
data.data,
vec![
0x00, 0xA0, 0xA0, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
]
);
}
_ => panic!("Expected S1 record"),
}
}
#[test]
fn test_parse_s2_record() {
let srec = "S214010480C04671B604207146014218D0EFF30983C5";
let rec = Record::parse_from_str(srec).unwrap();
match rec {
Record::S2(data) => {
assert_eq!(data.address, 0x010480);
assert_eq!(
data.data,
vec![
0xC0, 0x46, 0x71, 0xB6, 0x04, 0x20, 0x71, 0x46, 0x01, 0x42, 0x18, 0xD0,
0xEF, 0xF3, 0x09, 0x83
]
);
}
_ => panic!("Expected S1 record"),
}
}
}