use crate::checksum::checksum_of;
use crate::record::*;
use std::error;
use std::fmt;
use std::str::{self, FromStr};
#[derive(Debug, PartialEq)]
struct RawRecord {
t: u8,
bytes: Vec<u8>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub enum Error {
NotEnoughData,
UnexpectedCharacter,
ByteCountZero,
ChecksumMismatch,
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Error::NotEnoughData => "not enough data",
Error::UnexpectedCharacter => "unexpected character",
Error::ByteCountZero => "byte count zero",
Error::ChecksumMismatch => "checksum mismatch",
}
)
}
}
impl FromStr for RawRecord {
type Err = Error;
#[allow(clippy::len_zero)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() < 1 {
return Err(Error::NotEnoughData);
}
let (first_char, s) = s.split_at(1);
if first_char != "S" {
return Err(Error::UnexpectedCharacter);
}
if s.len() < 1 {
return Err(Error::NotEnoughData);
}
let (type_str, s) = s.split_at(1);
let t = type_str
.parse::<u8>()
.map_err(|_| Error::UnexpectedCharacter)?;
if s.len() < 2 {
return Err(Error::NotEnoughData);
}
let (byte_count_str, s) = s.split_at(2);
let byte_count =
usize::from_str_radix(byte_count_str, 16).map_err(|_| Error::UnexpectedCharacter)?;
if byte_count == 0 {
return Err(Error::ByteCountZero);
}
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::NotEnoughData);
}
let (byte_str, s2) = s.split_at(2);
s = s2;
bytes.push(u8::from_str_radix(byte_str, 16).map_err(|_| Error::UnexpectedCharacter)?);
}
let checksum = bytes.pop().unwrap();
let mut checksum_bytes = vec![byte_count as u8];
checksum_bytes.extend(&bytes);
let checksum_valid = checksum == checksum_of(&checksum_bytes);
if checksum_valid {
Ok(RawRecord { t, bytes })
} else {
Err(Error::ChecksumMismatch)
}
}
}
impl FromStr for Record {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let rr = RawRecord::from_str(s)?;
let r = match rr.t {
0 => Record::S0(
str::from_utf8(&rr.bytes[2..])
.expect("Invalid UTF-8 bytes in S0 data")
.trim_end_matches('\0')
.into(),
),
1 => {
if rr.bytes.len() < 2 {
return Err(Error::NotEnoughData);
}
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);
Record::S1(Data {
address: Address16(address),
data: data.to_vec(),
})
}
2 => {
if rr.bytes.len() < 3 {
return Err(Error::NotEnoughData);
}
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);
Record::S2(Data {
address: Address24(address),
data: data.to_vec(),
})
}
3 => {
if rr.bytes.len() < 4 {
return Err(Error::NotEnoughData);
}
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);
Record::S3(Data {
address: Address32(address),
data: data.to_vec(),
})
}
5 => {
if rr.bytes.len() != 2 {
return Err(Error::NotEnoughData);
}
let mut count = [0u8; 2];
count.copy_from_slice(&rr.bytes);
let count = u16::from_be_bytes(count);
Record::S5(Count16(count))
}
6 => {
if rr.bytes.len() != 3 {
return Err(Error::NotEnoughData);
}
let mut count = [0u8; 4];
count[1..].copy_from_slice(&rr.bytes);
let count = u32::from_be_bytes(count);
Record::S6(Count24(count))
}
7 => {
if rr.bytes.len() != 4 {
return Err(Error::NotEnoughData);
}
let mut address = [0u8; 4];
address.copy_from_slice(&rr.bytes);
let address = u32::from_be_bytes(address);
Record::S7(Address32(address))
}
8 => {
if rr.bytes.len() != 3 {
return Err(Error::NotEnoughData);
}
let mut address = [0u8; 4];
address[1..].copy_from_slice(&rr.bytes);
let address = u32::from_be_bytes(address);
Record::S8(Address24(address))
}
9 => {
if rr.bytes.len() != 2 {
return Err(Error::NotEnoughData);
}
let mut address = [0u8; 2];
address.copy_from_slice(&rr.bytes);
let address = u16::from_be_bytes(address);
Record::S9(Address16(address))
}
_ => return Err(Error::UnexpectedCharacter),
};
Ok(r)
}
}
pub fn read_records<'a>(s: &'a str) -> impl Iterator<Item = Result<Record, Error>> + 'a {
s.lines()
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.map(|line| line.parse::<Record>())
}
#[cfg(test)]
mod tests {
use super::*;
#[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::NotEnoughData));
}
#[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::UnexpectedCharacter));
}
#[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::NotEnoughData));
}
#[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::UnexpectedCharacter));
}
#[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::ByteCountZero));
}
#[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::UnexpectedCharacter));
}
#[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::NotEnoughData));
}
#[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));
}
#[test]
fn s0_empty_string_from_str_returns_correct_record() {
let s = "S0030000FC";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S0("".into())));
}
#[test]
fn s0_simple_string_from_str_returns_correct_record() {
let s = "S00600004844521B";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S0("HDR".into())));
}
#[test]
fn s0_null_terminated_string_from_str_returns_correct_record() {
let s = "S009000048445200000018";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S0("HDR".into())));
}
#[test]
fn s1_empty_from_str_returns_correct_record() {
let s = "S1031234B6";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S1(Data {
address: Address16(0x1234),
data: vec![]
}))
);
}
#[test]
fn s1_with_data_from_str_returns_correct_record() {
let s = "S107123400010203AC";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S1(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::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s2_empty_from_str_returns_correct_record() {
let s = "S2041234565F";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S2(Data {
address: Address24(0x123456),
data: vec![]
}))
);
}
#[test]
fn s2_with_data_from_str_returns_correct_record() {
let s = "S2081234560001020355";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S2(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::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s3_empty_from_str_returns_correct_record() {
let s = "S30512345678E6";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S3(Data {
address: Address32(0x12345678),
data: vec![]
}))
);
}
#[test]
fn s3_with_data_from_str_returns_correct_record() {
let s = "S3091234567800010203DC";
let r = s.parse::<Record>();
assert_eq!(
r,
Ok(Record::S3(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::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s5_returns_correct_record() {
let s = "S5031234B6";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S5(Count16(0x1234))));
}
#[test]
fn s5_invalid_from_str_returns_err_not_enough_data() {
let s = "S50212EB";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s6_returns_correct_record() {
let s = "S6041234565F";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S6(Count24(0x123456))));
}
#[test]
fn s6_invalid_from_str_returns_err_not_enough_data() {
let s = "S6031234B6";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s7_returns_correct_record() {
let s = "S70512345678E6";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S7(Address32(0x12345678))));
}
#[test]
fn s7_invalid_from_str_returns_err_not_enough_data() {
let s = "S7041234565F";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s8_returns_correct_record() {
let s = "S8041234565F";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S8(Address24(0x123456))));
}
#[test]
fn s8_invalid_from_str_returns_err_not_enough_data() {
let s = "S8031234B6";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn s9_returns_correct_record() {
let s = "S9031234B6";
let r = s.parse::<Record>();
assert_eq!(r, Ok(Record::S9(Address16(0x1234))));
}
#[test]
fn s9_invalid_from_str_returns_err_not_enough_data() {
let s = "S90212EB";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::NotEnoughData));
}
#[test]
fn record_from_str_returns_err_unexpected_character_on_unknown_type() {
let s = "S401FE";
let r = s.parse::<Record>();
assert_eq!(r, Err(Error::UnexpectedCharacter));
}
#[test]
fn read_records_empty_string_returns_empty_iterator() {
let s = "";
let mut ri = read_records(s);
assert_eq!(ri.next(), None);
}
#[test]
fn read_records_one_line_returns_iterator_with_one_item() {
let s = "S00600004844521B";
let mut ri = read_records(s);
assert_eq!(ri.next(), Some(Ok(Record::S0("HDR".into()))));
assert_eq!(ri.next(), None);
}
#[test]
fn read_records_one_line_with_trailing_newline_returns_iterator_with_one_item() {
let s = "S00600004844521B\n";
let mut ri = read_records(s);
assert_eq!(ri.next(), Some(Ok(Record::S0("HDR".into()))));
assert_eq!(ri.next(), None);
}
#[test]
fn read_records_one_line_with_empty_line_returns_iterator_with_one_item() {
let s = "S00600004844521B\n\n";
let mut ri = read_records(s);
assert_eq!(ri.next(), Some(Ok(Record::S0("HDR".into()))));
assert_eq!(ri.next(), None);
}
#[test]
fn read_records_multiple_lines_returns_iterator_containing_all() {
let s = "S00600004844521B\nS107123400010203AC";
let mut ri = read_records(s);
assert_eq!(ri.next(), Some(Ok(Record::S0("HDR".into()))));
assert_eq!(
ri.next(),
Some(Ok(Record::S1(Data {
address: Address16(0x1234),
data: vec![0x00, 0x01, 0x02, 0x03],
})))
);
assert_eq!(ri.next(), None);
}
}