use crate::lib_bitfield;
use crate::result::{Error, Result};
lib_bitfield! {
pub CardCommandClass(u16): u8 {
pub class11: 11;
pub class10: 10;
pub class9: 9;
pub class8: 8;
pub class7: 7;
pub class6: 6;
pub class5: 5;
pub class4: 4;
pub class3: 3;
pub class2: 2;
pub class1: 1;
pub class0: 0;
}
}
impl CardCommandClass {
pub const LEN: usize = 2;
pub const DEFAULT: u16 = 0x5b5;
pub(crate) const MASK: u16 = 0xfff;
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn from_bits(val: u16) -> Self {
Self(val & Self::MASK)
}
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::invalid_length(len, Self::LEN)),
_ => Ok(Self(u16::from_be_bytes([val[0], val[1]]) & Self::MASK)),
}
}
}
impl Default for CardCommandClass {
fn default() -> Self {
Self::new()
}
}
impl From<u16> for CardCommandClass {
fn from(val: u16) -> Self {
Self::from_bits(val)
}
}
impl From<CardCommandClass> for u16 {
fn from(val: CardCommandClass) -> Self {
val.bits()
}
}
impl From<CardCommandClass> for [u8; CardCommandClass::LEN] {
fn from(val: CardCommandClass) -> Self {
val.bytes()
}
}
impl TryFrom<&[u8]> for CardCommandClass {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<[u8; N]> for CardCommandClass {
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 CardCommandClass {
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 = CardCommandClass::new();
assert_eq!(ccc.bits(), CardCommandClass::DEFAULT);
assert_eq!(ccc.bytes(), CardCommandClass::DEFAULT.to_be_bytes());
test_field!(ccc, class0: 0);
test_field!(ccc, class1: 1);
test_field!(ccc, class2: 2);
test_field!(ccc, class3: 3);
test_field!(ccc, class4: 4);
test_field!(ccc, class5: 5);
test_field!(ccc, class6: 6);
test_field!(ccc, class7: 7);
test_field!(ccc, class8: 8);
test_field!(ccc, class9: 9);
test_field!(ccc, class10: 10);
test_field!(ccc, class11: 11);
(0..=0xfffu16).for_each(|raw| {
let raw_bytes = raw.to_be_bytes();
let exp_ccc = CardCommandClass(raw);
assert_eq!(CardCommandClass::from_bits(raw), exp_ccc);
assert_eq!(
CardCommandClass::try_from_bytes(raw_bytes.as_ref()),
Ok(exp_ccc)
);
assert_eq!(CardCommandClass::try_from(raw_bytes.as_ref()), Ok(exp_ccc));
assert_eq!(CardCommandClass::try_from(raw_bytes), Ok(exp_ccc));
assert_eq!(CardCommandClass::try_from(&raw_bytes), Ok(exp_ccc));
assert_eq!(exp_ccc.bits(), raw);
assert_eq!(exp_ccc.bytes(), raw_bytes);
(0..=11).for_each(|class| {
let is_supported = match class {
0 => exp_ccc.class0(),
1 => exp_ccc.class1(),
2 => exp_ccc.class2(),
3 => exp_ccc.class3(),
4 => exp_ccc.class4(),
5 => exp_ccc.class5(),
6 => exp_ccc.class6(),
7 => exp_ccc.class7(),
8 => exp_ccc.class8(),
9 => exp_ccc.class9(),
10 => exp_ccc.class10(),
_ => exp_ccc.class11(),
};
assert_eq!(is_supported, (raw & (1 << class)) != 0, "class: {class}",);
});
});
}
#[test]
fn test_invalid() {
let raw = CardCommandClass::new().bytes();
(0..CardCommandClass::LEN).for_each(|len| {
let exp_err = Error::invalid_length(len, CardCommandClass::LEN);
assert_eq!(
CardCommandClass::try_from_bytes(raw[..len].as_ref()),
Err(exp_err)
);
assert_eq!(
CardCommandClass::try_from(raw[..len].as_ref()),
Err(exp_err)
);
});
}
}