use std::{
fmt::{Display, Write},
str::FromStr,
};
use ansic::ansi;
use crate::{checksums::crc_srec, Error, Record};
const R: &str = ansi!(reset);
const GREEN: &str = ansi!(green);
const CYAN: &str = ansi!(cyan);
const YELLOW: &str = ansi!(yellow);
const BRIGHT_CYAN: &str = ansi!(br.cyan); const BRIGHT_MAGENTA: &str = ansi!(br.magenta); const BRIGHT_GREEN: &str = ansi!(br.green); const BRIGHT_YELLOW: &str = ansi!(br.yellow);
pub trait Address {
fn to_be_bytes(&self) -> Vec<u8>;
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Address16(pub u16);
impl Address for Address16 {
fn to_be_bytes(&self) -> Vec<u8> {
self.0.to_be_bytes().to_vec()
}
}
impl From<Address16> for u32 {
fn from(addr: Address16) -> u32 {
addr.0 as u32
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Address24(pub u32);
impl Address for Address24 {
fn to_be_bytes(&self) -> Vec<u8> {
self.0.to_be_bytes()[1..].to_vec()
}
}
impl From<Address24> for u32 {
fn from(addr: Address24) -> u32 {
addr.0
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Address32(pub u32);
impl Address for Address32 {
fn to_be_bytes(&self) -> Vec<u8> {
self.0.to_be_bytes().to_vec()
}
}
impl From<Address32> for u32 {
fn from(addr: Address32) -> u32 {
addr.0
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Count16(pub u16);
impl From<Count16> for u32 {
fn from(count: Count16) -> u32 {
count.0 as u32
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Count24(pub u32);
impl From<Count24> for u32 {
fn from(count: Count24) -> u32 {
count.0
}
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Data<T> {
pub address: T,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub enum SRecord {
Header(Vec<u8>),
Data16(Data<Address16>),
Data24(Data<Address24>),
Data32(Data<Address32>),
Count16(Count16),
Count24(Count24),
Address32(Address32),
Address24(Address24),
Address16(Address16),
}
#[derive(Debug, PartialEq)]
struct RawRecord {
t: u8,
bytes: Vec<u8>,
}
impl FromStr for RawRecord {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(Error::RecordTooShort);
}
let (first_char, s) = s.split_at(1);
if first_char != "S" {
return Err(Error::MissingStartCode);
}
if s.is_empty() {
return Err(Error::RecordTooShort);
}
let (type_str, s) = s.split_at(1);
let t = type_str
.parse::<u8>()
.map_err(|_| Error::ContainsInvalidCharacters)?;
if s.len() < 2 {
return Err(Error::RecordTooShort);
}
let (byte_count_str, s) = s.split_at(2);
let byte_count = usize::from_str_radix(byte_count_str, 16)
.map_err(|_| Error::ContainsInvalidCharacters)?;
if byte_count == 0 {
return Err(Error::RecordTooShort);
}
let mut bytes: Vec<u8> = Vec::with_capacity(byte_count);
let mut s = s;
for _ in 0..byte_count {
if s.len() < 2 {
return Err(Error::RecordTooShort);
}
let (byte_str, s2) = s.split_at(2);
s = s2;
bytes.push(
u8::from_str_radix(byte_str, 16).map_err(|_| Error::ContainsInvalidCharacters)?,
);
}
let checksum = bytes.pop().unwrap();
let mut checksum_bytes = vec![byte_count as u8];
checksum_bytes.extend(&bytes);
let checksum_valid = checksum == crc_srec(&checksum_bytes);
if checksum_valid {
Ok(RawRecord { t, bytes })
} else {
Err(Error::ChecksumMismatch(checksum, crc_srec(&checksum_bytes)))
}
}
}
impl FromStr for SRecord {
type Err = Error;
fn from_str(record_string: &str) -> Result<Self, Self::Err> {
Self::from_record_string(record_string)
}
}
fn make_record(t: u8, address: &impl Address, data: &[u8]) -> Result<String, Error> {
assert!(t < 10, "invalid record type {}", t);
let mut bytes = vec![0x00];
bytes.extend(address.to_be_bytes());
bytes.extend(data);
bytes[0] = (bytes.len()) as u8;
let result_length = 2 * bytes.len();
let data_str = String::with_capacity(result_length);
let data_str = bytes.iter().try_fold(data_str, |mut acc, byte| {
write!(&mut acc, "{:02X}", byte)
.map_err(|_| Error::SynthesisFailed)
.map(|_| acc)
})?;
Ok(format!("S{}{}{:02X}", t, data_str, crc_srec(&bytes)))
}
impl Record for SRecord {
fn to_record_string(&self) -> Result<String, Error> {
match self {
SRecord::Header(bytes) => make_record(0, &Address16(0x0000), bytes),
SRecord::Data16(Data { address, data }) => make_record(1, address, data),
SRecord::Data24(Data { address, data }) => make_record(2, address, data),
SRecord::Data32(Data { address, data }) => make_record(3, address, data),
SRecord::Count16(Count16(c)) => make_record(5, &Address16(*c), &[]),
SRecord::Count24(Count24(c)) => make_record(6, &Address24(*c), &[]),
SRecord::Address32(address) => make_record(7, address, &[]),
SRecord::Address24(address) => make_record(8, address, &[]),
SRecord::Address16(address) => make_record(9, address, &[]),
}
}
fn from_record_string<S>(record_string: S) -> Result<Self, Error>
where
S: AsRef<str>,
{
let rr = RawRecord::from_str(record_string.as_ref())?;
let r = match rr.t {
0 => SRecord::Header(rr.bytes[2..].to_vec()),
1 => {
if rr.bytes.len() < 2 {
return Err(Error::RecordTooShort);
}
let (address_bytes, data) = rr.bytes.split_at(2);
let mut address = [0u8; 2];
address.copy_from_slice(address_bytes);
let address = u16::from_be_bytes(address);
SRecord::Data16(Data {
address: Address16(address),
data: data.to_vec(),
})
}
2 => {
if rr.bytes.len() < 3 {
return Err(Error::RecordTooShort);
}
let (address_bytes, data) = rr.bytes.split_at(3);
let mut address = [0u8; 4];
address[1..].copy_from_slice(address_bytes);
let address = u32::from_be_bytes(address);
SRecord::Data24(Data {
address: Address24(address),
data: data.to_vec(),
})
}
3 => {
if rr.bytes.len() < 4 {
return Err(Error::RecordTooShort);
}
let (address_bytes, data) = rr.bytes.split_at(4);
let mut address = [0u8; 4];
address.copy_from_slice(address_bytes);
let address = u32::from_be_bytes(address);
SRecord::Data32(Data {
address: Address32(address),
data: data.to_vec(),
})
}
5 => {
if rr.bytes.len() != 2 {
return Err(Error::RecordTooShort);
}
let mut count = [0u8; 2];
count.copy_from_slice(&rr.bytes);
let count = u16::from_be_bytes(count);
SRecord::Count16(Count16(count))
}
6 => {
if rr.bytes.len() != 3 {
return Err(Error::RecordTooShort);
}
let mut count = [0u8; 4];
count[1..].copy_from_slice(&rr.bytes);
let count = u32::from_be_bytes(count);
SRecord::Count24(Count24(count))
}
7 => {
if rr.bytes.len() != 4 {
return Err(Error::RecordTooShort);
}
let mut address = [0u8; 4];
address.copy_from_slice(&rr.bytes);
let address = u32::from_be_bytes(address);
SRecord::Address32(Address32(address))
}
8 => {
if rr.bytes.len() != 3 {
return Err(Error::RecordTooShort);
}
let mut address = [0u8; 4];
address[1..].copy_from_slice(&rr.bytes);
let address = u32::from_be_bytes(address);
SRecord::Address24(Address24(address))
}
9 => {
if rr.bytes.len() != 2 {
return Err(Error::RecordTooShort);
}
let mut address = [0u8; 2];
address.copy_from_slice(&rr.bytes);
let address = u16::from_be_bytes(address);
SRecord::Address16(Address16(address))
}
_ => return Err(Error::ContainsInvalidCharacters),
};
Ok(r)
}
fn to_pretty_record_string(&self) -> Result<String, Error> {
let record_string = self.to_record_string()?;
let (width, type_str, type_txt) = match self {
SRecord::Header(_) => (
4,
format!("{BRIGHT_GREEN}{}{R}", &record_string[..2]),
" (header)",
),
SRecord::Data16(_) => (4, format!("{GREEN}{}{R}", &record_string[..2]), " (data)"),
SRecord::Data24(_) => (6, format!("{GREEN}{}{R}", &record_string[..2]), " (data)"),
SRecord::Data32(_) => (8, format!("{GREEN}{}{R}", &record_string[..2]), " (data)"),
SRecord::Count16(_) => (
4,
format!("{BRIGHT_YELLOW}{}{R}", &record_string[..2]),
" (count)",
),
SRecord::Count24(_) => (
6,
format!("{BRIGHT_YELLOW}{}{R}", &record_string[..2]),
" (count)",
),
SRecord::Address32(_) => (
8,
format!("{BRIGHT_CYAN}{}{R}", &record_string[..2]),
" (start address)",
),
SRecord::Address24(_) => (
6,
format!("{BRIGHT_CYAN}{}{R}", &record_string[..2]),
" (start address)",
),
SRecord::Address16(_) => (
4,
format!("{BRIGHT_CYAN}{}{R}", &record_string[..2]),
" (start address)",
),
};
Ok(format!(
"{}{BRIGHT_MAGENTA}{}{R}{YELLOW}{}{R}{}{CYAN}{}{R}{}",
type_str,
&record_string[2..4],
&record_string[4..4 + width],
&record_string[4 + width..record_string.len() - 2],
&record_string[record_string.len() - 2..],
type_txt
))
}
}
impl Display for SRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SRecord::Header(header) => {
write!(
f,
"Header Record: Header: {}",
String::from_utf8(header.to_vec()).map_err(|_err| std::fmt::Error)?
)
}
SRecord::Data16(Data { address, data }) => write!(
f,
"Data Record with 16bit Address: Address: {}, Data: {:?}",
address.0, data
),
SRecord::Data24(Data { address, data }) => write!(
f,
"Data Record with 24bit Address: Address: {}, Data: {:?}",
address.0, data
),
SRecord::Data32(Data { address, data }) => write!(
f,
"Data Record with 32bit Address: Address: {}, Data: {:?}",
address.0, data
),
SRecord::Count16(count) => {
write!(f, "Count Record with 16bit Counter: Count: {}", count.0)
}
SRecord::Count24(count) => {
write!(f, "Count Record with 24bit Counter: Count: {}", count.0)
}
SRecord::Address32(address) => {
write!(
f,
"Start Address Record with 32bit Address: Address: {}",
address.0
)
}
SRecord::Address24(address) => {
write!(
f,
"Start Address Record with 24bit Address: Address: {}",
address.0
)
}
SRecord::Address16(address) => {
write!(
f,
"Start Address Record with 16bit Address: Address: {}",
address.0
)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn address16_to_be_bytes() {
let a = Address16(0x1234);
let b = a.to_be_bytes();
assert_eq!(b, [0x12, 0x34]);
}
#[test]
fn address16_into_u32() {
let a = Address16(0x1234);
let b: u32 = a.into();
assert_eq!(b, 0x1234u32);
}
#[test]
fn address24_to_be_bytes() {
let a = Address24(0x123456);
let b = a.to_be_bytes();
assert_eq!(b, [0x12, 0x34, 0x56]);
}
#[test]
fn address24_into_u32() {
let a = Address24(0x123456);
let b: u32 = a.into();
assert_eq!(b, 0x123456u32);
}
#[test]
fn address32_to_be_bytes() {
let a = Address32(0x12345678);
let b = a.to_be_bytes();
assert_eq!(b, [0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn address32_into_u32() {
let a = Address32(0x12345678);
let b: u32 = a.into();
assert_eq!(b, 0x12345678u32);
}
#[test]
fn count16_into_u32() {
let a = Count16(0x1234);
let b: u32 = a.into();
assert_eq!(b, 0x1234);
}
#[test]
fn count24_into_u32() {
let a = Count24(0x123456);
let b: u32 = a.into();
assert_eq!(b, 0x123456);
}
#[test]
fn raw_record_from_str_empty_str_returns_err_not_enough_data() {
let s = "";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::RecordTooShort));
}
#[test]
fn raw_record_from_str_first_character_invalid_returns_err_unexpected_character() {
let s = "D";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::MissingStartCode));
}
#[test]
fn raw_record_from_str_no_type_value_returns_err_not_enough_data() {
let s = "S";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::RecordTooShort));
}
#[test]
fn raw_record_from_str_invalid_type_value_returns_err_unexpected_character() {
let s = "Sx";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::ContainsInvalidCharacters));
}
#[test]
fn raw_record_from_str_byte_count_zero_returns_err_byte_count_zero() {
let s = "S100";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::RecordTooShort));
}
#[test]
fn raw_record_from_str_invalid_hex_character_returns_err_unexpected_character() {
let s = "S104123400xx";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::ContainsInvalidCharacters));
}
#[test]
fn raw_record_from_str_byte_count_too_large_returns_err_not_enough_data() {
let s = "S1100000FFEF";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::RecordTooShort));
}
#[test]
fn raw_record_from_str_valid_record_empty_returns_ok_correct_raw_record() {
let s = "S101FE";
let rr = RawRecord::from_str(s);
assert_eq!(
rr,
Ok(RawRecord {
t: 1,
bytes: vec![]
})
);
}
#[test]
fn raw_record_from_str_valid_record_valid_checksum_returns_ok_correct_raw_record() {
let s = "S1101234000102030405060708090A0B0C5B";
let rr = RawRecord::from_str(s);
assert_eq!(
rr,
Ok(RawRecord {
t: 1,
bytes: vec![
0x12, 0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c
],
})
);
}
#[test]
fn raw_record_from_str_valid_record_invalid_checksum_returns_ok_correct_raw_record() {
let s = "S1101234000102030405060708090A0B0CFF";
let rr = RawRecord::from_str(s);
assert_eq!(rr, Err(Error::ChecksumMismatch(0xFF, 0x5B)));
}
#[test]
fn s0_empty_string_from_str_returns_correct_record() {
let s = "S0030000FC";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Header("".into())));
}
#[test]
fn s0_simple_string_from_str_returns_correct_record() {
let s = "S00600004844521B";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Header("HDR".into())));
}
#[test]
fn s0_null_terminated_string_from_str_returns_correct_record() {
let s = "S009000048445200000018";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Header(
[0x48, 0x44, 0x52, 0x00, 0x00, 0x00].to_vec()
))
);
}
#[test]
fn s1_empty_from_str_returns_correct_record() {
let s = "S1031234B6";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data16(Data {
address: Address16(0x1234),
data: vec![]
}))
);
}
#[test]
fn s1_with_data_from_str_returns_correct_record() {
let s = "S107123400010203AC";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data16(Data {
address: Address16(0x1234),
data: vec![0x00, 0x01, 0x02, 0x03]
}))
);
}
#[test]
fn s1_invalid_from_str_returns_err_not_enough_data() {
let s = "S10212EB";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s2_empty_from_str_returns_correct_record() {
let s = "S2041234565F";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data24(Data {
address: Address24(0x123456),
data: vec![]
}))
);
}
#[test]
fn s2_with_data_from_str_returns_correct_record() {
let s = "S2081234560001020355";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data24(Data {
address: Address24(0x123456),
data: vec![0x00, 0x01, 0x02, 0x03]
}))
);
}
#[test]
fn s2_invalid_from_str_returns_err_not_enough_data() {
let s = "S2031234B6";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s3_empty_from_str_returns_correct_record() {
let s = "S30512345678E6";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data32(Data {
address: Address32(0x12345678),
data: vec![]
}))
);
}
#[test]
fn s3_with_data_from_str_returns_correct_record() {
let s = "S3091234567800010203DC";
let r = s.parse::<SRecord>();
assert_eq!(
r,
Ok(SRecord::Data32(Data {
address: Address32(0x12345678),
data: vec![0x00, 0x01, 0x02, 0x03]
}))
);
}
#[test]
fn s3_invalid_from_str_returns_err_not_enough_data() {
let s = "S3041234565F";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s5_returns_correct_record() {
let s = "S5031234B6";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Count16(Count16(0x1234))));
}
#[test]
fn s5_invalid_from_str_returns_err_not_enough_data() {
let s = "S50212EB";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s6_returns_correct_record() {
let s = "S6041234565F";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Count24(Count24(0x123456))));
}
#[test]
fn s6_invalid_from_str_returns_err_not_enough_data() {
let s = "S6031234B6";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s7_returns_correct_record() {
let s = "S70512345678E6";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Address32(Address32(0x12345678))));
}
#[test]
fn s7_invalid_from_str_returns_err_not_enough_data() {
let s = "S7041234565F";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s8_returns_correct_record() {
let s = "S8041234565F";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Address24(Address24(0x123456))));
}
#[test]
fn s8_invalid_from_str_returns_err_not_enough_data() {
let s = "S8031234B6";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn s9_returns_correct_record() {
let s = "S9031234B6";
let r = s.parse::<SRecord>();
assert_eq!(r, Ok(SRecord::Address16(Address16(0x1234))));
}
#[test]
fn s9_invalid_from_str_returns_err_not_enough_data() {
let s = "S90212EB";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::RecordTooShort));
}
#[test]
fn record_from_str_returns_err_unexpected_character_on_unknown_type() {
let s = "S401FE";
let r = s.parse::<SRecord>();
assert_eq!(r, Err(Error::ContainsInvalidCharacters));
}
#[test]
fn encode_s0_empty_string_returns_empty_record() {
let r = SRecord::Header("".into());
let s = r.to_record_string();
assert_eq!(s, Ok("S0030000FC".into()));
}
#[test]
fn encode_s0_simple_string_returns_correct_record() {
let r = SRecord::Header("HDR".into());
let s = r.to_record_string();
assert_eq!(s, Ok("S00600004844521B".into()));
}
#[test]
fn encode_s1_empty_returns_empty_record() {
let r = SRecord::Data16(Data {
address: Address16(0x1234),
data: vec![],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S1031234B6".into()));
}
#[test]
fn encode_s1_with_data_returns_correct_record() {
let r = SRecord::Data16(Data {
address: Address16(0x1234),
data: vec![0x00, 0x01, 0x02, 0x03],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S107123400010203AC".into()));
}
#[test]
fn encode_s2_empty_returns_empty_record() {
let r = SRecord::Data24(Data {
address: Address24(0x123456),
data: vec![],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S2041234565F".into()));
}
#[test]
fn encode_s2_with_data_returns_correct_record() {
let r = SRecord::Data24(Data {
address: Address24(0x123456),
data: vec![0x00, 0x01, 0x02, 0x03],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S2081234560001020355".into()));
}
#[test]
fn encode_s3_empty_returns_empty_record() {
let r = SRecord::Data32(Data {
address: Address32(0x12345678),
data: vec![],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S30512345678E6".into()));
}
#[test]
fn encode_s3_with_data_returns_correct_record() {
let r = SRecord::Data32(Data {
address: Address32(0x12345678),
data: vec![0x00, 0x01, 0x02, 0x03],
});
let s = r.to_record_string();
assert_eq!(s, Ok("S3091234567800010203DC".into()));
}
#[test]
fn encode_s5_returns_correct_record() {
let r = SRecord::Count16(Count16(0x1234));
let s = r.to_record_string();
assert_eq!(s, Ok("S5031234B6".into()));
}
#[test]
fn encode_s6_returns_correct_record() {
let r = SRecord::Count24(Count24(0x123456));
let s = r.to_record_string();
assert_eq!(s, Ok("S6041234565F".into()));
}
#[test]
fn encode_s7_returns_correct_record() {
let r = SRecord::Address32(Address32(0x12345678));
let s = r.to_record_string();
assert_eq!(s, Ok("S70512345678E6".into()));
}
#[test]
fn encode_s8_returns_correct_record() {
let r = SRecord::Address24(Address24(0x123456));
let s = r.to_record_string();
assert_eq!(s, Ok("S8041234565F".into()));
}
#[test]
fn encode_s9_returns_correct_record() {
let r = SRecord::Address16(Address16(0x1234));
let s = r.to_record_string();
assert_eq!(s, Ok("S9031234B6".into()));
}
}