use crate::lib_bitfield;
use crate::result::{Error, Result};
const DEFAULT_CCC_RBL: u16 = 0x5b50;
lib_bitfield! {
pub CommandClassReadBlockLen(u16): u8 {
pub class11: 15;
pub class10: 14;
pub class9: 13;
pub class8: 12;
pub class7: 11;
pub class6: 10;
pub class5: 9;
pub class4: 8;
pub class3: 7;
pub class2: 6;
pub class1: 5;
pub class0: 4;
pub read_data_block_len: 3, 0;
}
}
impl CommandClassReadBlockLen {
pub const LEN: usize = 2;
pub const fn new() -> Self {
Self(DEFAULT_CCC_RBL)
}
pub const fn bits(&self) -> u16 {
self.0
}
pub const fn from_bits(val: u16) -> Self {
Self(val)
}
pub const fn bytes(&self) -> [u8; Self::LEN] {
self.0.to_be_bytes()
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < Self::LEN => Err(Error::InvalidVariant(len)),
_ => Ok(Self(u16::from_be_bytes([val[0], val[1]]))),
}
}
}
impl Default for CommandClassReadBlockLen {
fn default() -> Self {
Self::new()
}
}
impl From<u16> for CommandClassReadBlockLen {
fn from(val: u16) -> Self {
Self::from_bits(val)
}
}
impl From<CommandClassReadBlockLen> for u16 {
fn from(val: CommandClassReadBlockLen) -> Self {
val.bits()
}
}
impl From<CommandClassReadBlockLen> for [u8; CommandClassReadBlockLen::LEN] {
fn from(val: CommandClassReadBlockLen) -> Self {
val.bytes()
}
}
impl TryFrom<&[u8]> for CommandClassReadBlockLen {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<[u8; N]> for CommandClassReadBlockLen {
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 CommandClassReadBlockLen {
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;
#[test]
fn test_valid() {
let mut ccc_rbl = CommandClassReadBlockLen::new();
assert_eq!(ccc_rbl.bits(), DEFAULT_CCC_RBL);
assert_eq!(ccc_rbl.bytes(), DEFAULT_CCC_RBL.to_be_bytes());
test_field!(ccc_rbl, class0: 4);
test_field!(ccc_rbl, class1: 5);
test_field!(ccc_rbl, class2: 6);
test_field!(ccc_rbl, class3: 7);
test_field!(ccc_rbl, class4: 8);
test_field!(ccc_rbl, class5: 9);
test_field!(ccc_rbl, class6: 10);
test_field!(ccc_rbl, class7: 11);
test_field!(ccc_rbl, class8: 12);
test_field!(ccc_rbl, class9: 13);
test_field!(ccc_rbl, class10: 14);
test_field!(ccc_rbl, class11: 15);
(0..=u16::MAX).for_each(|raw| {
let raw_bytes = raw.to_be_bytes();
let exp_ccc_rbl = CommandClassReadBlockLen(raw);
let exp_rbl = raw & 0xf;
assert_eq!(CommandClassReadBlockLen::from_bits(raw), exp_ccc_rbl);
assert_eq!(
CommandClassReadBlockLen::try_from_bytes(raw_bytes.as_ref()),
Ok(exp_ccc_rbl)
);
assert_eq!(
CommandClassReadBlockLen::try_from(raw_bytes.as_ref()),
Ok(exp_ccc_rbl)
);
assert_eq!(
CommandClassReadBlockLen::try_from(raw_bytes),
Ok(exp_ccc_rbl)
);
assert_eq!(
CommandClassReadBlockLen::try_from(&raw_bytes),
Ok(exp_ccc_rbl)
);
assert_eq!(exp_ccc_rbl.bits(), raw);
assert_eq!(exp_ccc_rbl.bytes(), raw_bytes);
assert_eq!(exp_ccc_rbl.read_data_block_len(), exp_rbl);
(4..=15).for_each(|class| {
let is_supported = match class {
4 => exp_ccc_rbl.class0(),
5 => exp_ccc_rbl.class1(),
6 => exp_ccc_rbl.class2(),
7 => exp_ccc_rbl.class3(),
8 => exp_ccc_rbl.class4(),
9 => exp_ccc_rbl.class5(),
10 => exp_ccc_rbl.class6(),
11 => exp_ccc_rbl.class7(),
12 => exp_ccc_rbl.class8(),
13 => exp_ccc_rbl.class9(),
14 => exp_ccc_rbl.class10(),
_ => exp_ccc_rbl.class11(),
};
assert_eq!(
is_supported,
(raw & (1 << class)) != 0,
"class: {}",
class - 4
);
});
});
}
#[test]
fn test_invalid() {
let raw = CommandClassReadBlockLen::new().bytes();
(0..CommandClassReadBlockLen::LEN).for_each(|len| {
let exp_err = Error::InvalidVariant(len);
assert_eq!(
CommandClassReadBlockLen::try_from_bytes(raw[..len].as_ref()),
Err(exp_err)
);
assert_eq!(
CommandClassReadBlockLen::try_from(raw[..len].as_ref()),
Err(exp_err)
);
});
}
}