use crate::crc::Crc7;
use crate::result::{Error, Result};
use crate::{lib_bitfield, response};
mod flags;
pub use flags::*;
const COMMAND_INDEX: u8 = 0b11_0100;
lib_bitfield! {
pub R5(MSB0 [u8; 6]): u8 {
start: 47;
direction: 46;
raw_command_index: 45, 40;
raw_flags: 23, 16;
pub data: 15, 8;
raw_crc: 7, 1;
end: 0;
}
}
response! {
R5 {
response_mode: Sd,
}
}
impl R5 {
pub const LEN: usize = 6;
pub const DEFAULT: [u8; Self::LEN] = [0x34, 0, 0, 0, 0, 0x45];
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn command_index(&self) -> u8 {
self.raw_command_index()
}
pub const fn flags(&self) -> Result<Flags> {
Flags::try_from_bits(self.raw_flags())
}
pub fn set_flags(&mut self, val: Flags) {
self.set_raw_flags(val.bits());
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
_ => {
let raw_crc = val[5] >> 1;
let exp_crc = Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]).bits();
match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
r5 if r5.start() => Err(Error::invalid_field_variant("r5::start", 1)),
r5 if r5.direction() => Err(Error::invalid_field_variant("r5::direction", 1)),
r5 if r5.command_index() != COMMAND_INDEX => Err(Error::invalid_field_variant(
"r5::command_index",
r5.command_index() as usize,
)),
r5 if r5.flags().is_err() => Err(Error::invalid_field_variant(
"r5::flags",
r5.raw_flags() as usize,
)),
r5 if raw_crc != exp_crc => Err(Error::invalid_crc7(raw_crc, exp_crc)),
r5 if !r5.end() => Err(Error::invalid_field_variant("r5::end", 0)),
r5 => Ok(r5),
}
}
}
}
}
impl Default for R5 {
fn default() -> Self {
Self::new()
}
}
impl TryFrom<&[u8]> for R5 {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<[u8; N]> for R5 {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
impl<const N: usize> TryFrom<&[u8; N]> for R5 {
type Error = Error;
fn try_from(val: &[u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_field;
use crate::util::raw_with_crc;
#[test]
fn test_fields() {
let mut r5 = R5::new();
let mut flags = r5.flags().unwrap();
assert!(!flags.out_of_range());
assert!(!flags.function_number());
assert!(!flags.error());
assert!(!flags.illegal_command());
assert!(!flags.crc_error());
test_field!(flags, out_of_range: 0);
test_field!(flags, function_number: 1);
test_field!(flags, error: 3);
test_field!(flags, illegal_command: 6);
test_field!(flags, crc_error: 7);
assert_eq!(flags.io_current_state(), Ok(IoCurrentState::new()));
assert_eq!(r5.data(), 0);
(0..=u8::MAX)
.zip(0..=u8::MAX)
.for_each(|(raw_flags, data)| {
let (raw, crc) = raw_with_crc([COMMAND_INDEX, 0, 0, raw_flags, data, 0]);
match Flags::try_from_bits(raw_flags) {
Ok(flags) => {
r5 = R5(raw);
assert_eq!(R5::try_from_bytes(raw.as_ref()), Ok(r5));
assert_eq!(R5::try_from(raw), Ok(r5));
assert_eq!(r5.flags(), Ok(flags));
assert_eq!(r5.data(), data);
r5.set_flags(Flags::new());
assert_eq!(r5.flags(), Ok(Flags::new()));
r5.set_flags(flags);
assert_eq!(r5.flags(), Ok(flags));
r5.set_data(0);
assert_eq!(r5.data(), 0);
r5.set_data(data);
assert_eq!(r5.data(), data);
}
Err(_) => {
assert_eq!(
R5::try_from_bytes(raw.as_ref()),
Err(Error::invalid_field_variant(
"r5::flags",
raw_flags as usize
))
);
assert_eq!(
R5::try_from(raw),
Err(Error::invalid_field_variant(
"r5::flags",
raw_flags as usize
))
);
}
}
});
}
}