use crate::lib_struct;
use crate::result::{Error, Result};
use crate::crc::Crc7;
pub use crate::response::End as Crc;
mod mdt;
mod mid;
mod oid;
mod pnm;
mod prv;
mod psn;
pub use mdt::*;
pub use mid::*;
pub use oid::*;
pub use pnm::*;
pub use prv::*;
pub use psn::*;
pub const CID_LEN: usize = 16;
lib_struct! {
Cid {
mid: ManufacturerId,
oid: OemId,
pnm: ProductName,
prv: ProductRevision,
psn: ProductSerialNumber,
mdt: ManufacturingDate,
crc: Crc,
}
}
impl Cid {
pub const LEN: usize = 16;
pub const fn new() -> Self {
Self {
mid: ManufacturerId::new(),
oid: OemId::new(),
pnm: ProductName::new(),
prv: ProductRevision::new(),
psn: ProductSerialNumber::new(),
mdt: ManufacturingDate::new(),
crc: Crc::from_bits(0x13),
}
}
pub const fn bytes(&self) -> [u8; CID_LEN] {
let mid = self.mid.bits();
let [oid0, oid1] = self.oid.bytes();
let [pnm0, pnm1, pnm2, pnm3, pnm4] = self.pnm.bytes();
let prv = self.prv.bits();
let [psn0, psn1, psn2, psn3] = self.psn.bytes();
let [mdt0, mdt1] = self.mdt.bytes();
let crc = self.crc.bits();
[
mid, oid0, oid1, pnm0, pnm1, pnm2, pnm3, pnm4, prv, psn0, psn1, psn2, psn3, mdt0, mdt1,
crc,
]
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < CID_LEN => Err(Error::InvalidVariant(len)),
_ => {
match (
ManufacturerId::from_bits(val[0]),
OemId::try_from_bytes(&[val[1], val[2]]),
ProductName::try_from_bytes(&[val[3], val[4], val[5], val[6], val[7]]),
ProductRevision::from_bits(val[8]),
ProductSerialNumber::from_bits(u32::from_be_bytes([
val[9], val[10], val[11], val[12],
])),
ManufacturingDate::try_from_bytes(&[val[13], val[14]]),
Crc::try_from_bits(val[15]),
) {
(mid, Ok(oid), Ok(pnm), prv, psn, Ok(mdt), Ok(crc)) => Ok(Self {
mid,
oid,
pnm,
prv,
psn,
mdt,
crc,
}),
(_, Err(err), _, _, _, _, _) => Err(err),
(_, _, Err(err), _, _, _, _) => Err(err),
(_, _, _, _, _, Err(err), _) => Err(err),
(_, _, _, _, _, _, Err(err)) => Err(err),
}
}
}
}
pub const fn validate_crc(self) -> Result<Self> {
let mid = self.mid.bits();
let [oid0, oid1] = self.oid.bytes();
let [pnm0, pnm1, pnm2, pnm3, pnm4] = self.pnm.bytes();
let prv = self.prv.bits();
let [psn0, psn1, psn2, psn3] = self.psn.bytes();
let [mdt0, mdt1] = self.mdt.bytes();
let exp_crc = self.crc.crc();
let calc_crc = Crc7::calculate(&[
mid, oid0, oid1, pnm0, pnm1, pnm2, pnm3, pnm4, prv, psn0, psn1, psn2, psn3, mdt0, mdt1,
]);
if calc_crc.bits() == calc_crc.bits() {
Ok(self)
} else {
Err(Error::InvalidCrc7 {
calculated: calc_crc.bits(),
invalid: exp_crc.bits(),
})
}
}
}
impl Default for Cid {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn raw_with_crc(mut raw: [u8; CID_LEN]) -> ([u8; CID_LEN], Crc) {
let crc7 = Crc7::calculate(&raw[..15]);
let raw_crc = (crc7.bits() << 1) | 1;
raw[15] = raw_crc;
(raw, Crc::try_from_bits(raw_crc).unwrap())
}
#[test]
fn test_valid_default() {
let (raw, crc) = raw_with_crc([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
let raw_crc = 0x13;
let exp_cid = Cid {
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(crc.bits(), raw_crc);
}
#[test]
fn test_valid_mid() {
(0..=u8::MAX).for_each(|raw_mid| {
let mid = ManufacturerId::from_bits(raw_mid);
let (raw, crc) = raw_with_crc([raw_mid, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
let exp_cid = Cid {
mid,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.mid(), mid);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.oid(), Default::default());
assert_eq!(exp_cid.pnm(), Default::default());
assert_eq!(exp_cid.prv(), Default::default());
assert_eq!(exp_cid.psn(), Default::default());
assert_eq!(exp_cid.mdt(), Default::default());
});
}
#[test]
fn test_valid_oid() {
(0..=u16::MAX)
.filter_map(|c| {
let bytes = c.to_be_bytes();
if bytes.iter().all(|&c| (c as char).is_ascii()) {
Some(bytes)
} else {
None
}
})
.for_each(|bytes| {
let [oid0, oid1] = bytes;
let oid = OemId::try_from_bytes(bytes.as_ref()).unwrap();
let (raw, crc) =
raw_with_crc([0, oid0, oid1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
let exp_cid = Cid {
oid,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.oid(), oid);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.mid(), Default::default());
assert_eq!(exp_cid.pnm(), Default::default());
assert_eq!(exp_cid.prv(), Default::default());
assert_eq!(exp_cid.psn(), Default::default());
assert_eq!(exp_cid.mdt(), Default::default());
});
}
#[test]
fn test_valid_pnm() {
(0..=u16::MAX)
.filter_map(|c| {
let bytes = c.to_be_bytes();
if bytes.iter().all(|&c| (c as char).is_ascii()) {
Some(bytes)
} else {
None
}
})
.for_each(|bytes| {
let [pnm0, pnm1] = bytes;
let raw_pnm = [pnm0, pnm1, pnm0, pnm1, pnm0];
let pnm = ProductName::try_from_bytes(raw_pnm.as_ref()).unwrap();
let (raw, crc) = raw_with_crc([
0, 0, 0, pnm0, pnm1, pnm0, pnm1, pnm0, 0, 0, 0, 0, 0, 0, 1, 0,
]);
let exp_cid = Cid {
pnm,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.pnm(), pnm);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.mid(), Default::default());
assert_eq!(exp_cid.oid(), Default::default());
assert_eq!(exp_cid.prv(), Default::default());
assert_eq!(exp_cid.psn(), Default::default());
assert_eq!(exp_cid.mdt(), Default::default());
});
}
#[test]
fn test_valid_prv() {
(0..=u8::MAX).for_each(|raw_prv| {
let prv = ProductRevision::from_bits(raw_prv);
let (raw, crc) = raw_with_crc([0, 0, 0, 0, 0, 0, 0, 0, raw_prv, 0, 0, 0, 0, 0, 1, 0]);
let exp_cid = Cid {
prv,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.prv(), prv);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.mid(), Default::default());
assert_eq!(exp_cid.oid(), Default::default());
assert_eq!(exp_cid.pnm(), Default::default());
assert_eq!(exp_cid.psn(), Default::default());
assert_eq!(exp_cid.mdt(), Default::default());
});
}
#[test]
fn test_valid_psn() {
(0..=u16::MAX)
.filter_map(|c| {
let bytes = c.to_be_bytes();
if bytes.iter().all(|&c| (c as char).is_ascii()) {
Some(bytes)
} else {
None
}
})
.for_each(|bytes| {
let [psn0, psn1] = bytes;
let raw_psn = [psn0, psn1, psn0, psn1];
let psn = ProductSerialNumber::try_from_bytes(raw_psn.as_ref()).unwrap();
let (raw, crc) =
raw_with_crc([0, 0, 0, 0, 0, 0, 0, 0, 0, psn0, psn1, psn0, psn1, 0, 1, 0]);
let exp_cid = Cid {
psn,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.psn(), psn);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.mid(), Default::default());
assert_eq!(exp_cid.oid(), Default::default());
assert_eq!(exp_cid.pnm(), Default::default());
assert_eq!(exp_cid.prv(), Default::default());
assert_eq!(exp_cid.mdt(), Default::default());
});
}
#[test]
fn test_valid_mdt() {
(0..=0xfff).for_each(|raw_mdt| {
let year = (raw_mdt >> 8) as u8;
let month = ((raw_mdt & 0xf0) | ((raw_mdt % 12) + 1)) as u8;
let mdt = ManufacturingDate::try_from_bytes([year, month].as_ref()).unwrap();
let (raw, crc) = raw_with_crc([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, year, month, 0]);
let exp_cid = Cid {
mdt,
crc,
..Default::default()
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.mdt(), mdt);
assert_eq!(exp_cid.crc(), crc);
assert_eq!(exp_cid.mid(), Default::default());
assert_eq!(exp_cid.oid(), Default::default());
assert_eq!(exp_cid.pnm(), Default::default());
assert_eq!(exp_cid.prv(), Default::default());
assert_eq!(exp_cid.psn(), Default::default());
});
}
#[test]
fn test_valid_crc() {
(0..=u8::MAX)
.filter(|&c| (c as char).is_ascii())
.for_each(|raw_val| {
let (mut raw, crc) = raw_with_crc([raw_val; CID_LEN]);
let mid = ManufacturerId::from_bits(raw[0]);
let oid = OemId::try_from_bytes(raw[1..=2].as_ref()).unwrap();
let pnm = ProductName::try_from_bytes(raw[3..=7].as_ref()).unwrap();
let prv = ProductRevision::from_bits(raw[8]);
let psn = ProductSerialNumber::from_bits(u32::from_be_bytes(
raw[9..=12].try_into().unwrap(),
));
let year = (raw_val & 0xf0) >> 4;
let month = (raw_val & 0xf0) | ((raw_val % 12) + 1);
let mdt = ManufacturingDate::try_from_bytes([year, month].as_ref()).unwrap();
raw[13] = year;
raw[14] = month;
let exp_cid = Cid {
mid,
oid,
pnm,
prv,
psn,
mdt,
crc,
};
assert_eq!(Cid::try_from_bytes(raw.as_ref()), Ok(exp_cid));
assert_eq!(exp_cid.bytes(), raw);
assert_eq!(exp_cid.mid(), mid);
assert_eq!(exp_cid.oid(), oid);
assert_eq!(exp_cid.pnm(), pnm);
assert_eq!(exp_cid.prv(), prv);
assert_eq!(exp_cid.psn(), psn);
assert_eq!(exp_cid.mdt(), mdt);
assert_eq!(exp_cid.crc(), crc);
});
}
}