use crate::lib_bitfield;
use crate::result::{Error, Result};
lib_bitfield! {
pub BlockUnitCount(u16): u16 {
raw_count: 8, 0;
}
}
impl BlockUnitCount {
pub const LEN: usize = 2;
pub const DEFAULT: u16 = 0;
pub const MIN: u16 = 1;
pub const MAX: u16 = 512;
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn count(&self) -> u16 {
self.raw_count() + 1
}
pub fn set_count(&mut self, val: u16) {
self.set_raw_count(val.saturating_sub(1));
}
pub const fn from_bits_unchecked(val: u16) -> Self {
Self(val)
}
pub const fn try_from_bits(val: u16) -> Result<Self> {
match val {
v if v < Self::MIN || v > Self::MAX => Err(Error::invalid_field_value(
"command::argument::buc",
v as usize,
Self::MIN as usize,
Self::MAX as usize,
)),
_ => Ok(Self(val - 1)),
}
}
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)),
_ => Self::try_from_bits(u16::from_be_bytes([val[0], val[1]])),
}
}
pub const fn bytes(&self) -> [u8; Self::LEN] {
self.0.to_be_bytes()
}
}
impl Default for BlockUnitCount {
fn default() -> Self {
Self::new()
}
}
impl From<BlockUnitCount> for u16 {
fn from(val: BlockUnitCount) -> Self {
val.bits()
}
}
impl From<BlockUnitCount> for [u8; BlockUnitCount::LEN] {
fn from(val: BlockUnitCount) -> Self {
val.bytes()
}
}
impl TryFrom<u16> for BlockUnitCount {
type Error = Error;
fn try_from(val: u16) -> Result<Self> {
Self::try_from_bits(val)
}
}
impl TryFrom<&[u8]> for BlockUnitCount {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<&[u8; N]> for BlockUnitCount {
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 BlockUnitCount {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fields() {
(BlockUnitCount::MIN..=BlockUnitCount::MAX).for_each(|raw_count| {
let raw = raw_count.to_be_bytes();
let exp_bits = raw_count - 1;
let exp_bytes = exp_bits.to_be_bytes();
let exp_buc = BlockUnitCount::from_bits_unchecked(exp_bits);
assert_eq!(BlockUnitCount::try_from_bits(raw_count), Ok(exp_buc));
assert_eq!(BlockUnitCount::try_from_bytes(&raw), Ok(exp_buc));
assert_eq!(BlockUnitCount::try_from(raw_count), Ok(exp_buc));
assert_eq!(BlockUnitCount::try_from(&raw), Ok(exp_buc));
assert_eq!(BlockUnitCount::try_from(raw), Ok(exp_buc));
assert_eq!(exp_buc.bits(), exp_bits);
assert_eq!(exp_buc.count(), raw_count);
});
(0..=u16::BITS)
.filter_map(|r| match ((1u32 << r) - 1) as u16 {
count if (BlockUnitCount::MIN..=BlockUnitCount::MAX).contains(&count) => None,
count => Some(count),
})
.for_each(|invalid_count| {
let invalid_bytes = invalid_count.to_be_bytes();
let exp_err = Error::invalid_field_value(
"command::argument::buc",
invalid_count as usize,
BlockUnitCount::MIN as usize,
BlockUnitCount::MAX as usize,
);
assert_eq!(BlockUnitCount::try_from_bits(invalid_count), Err(exp_err));
assert_eq!(BlockUnitCount::try_from_bytes(&invalid_bytes), Err(exp_err));
assert_eq!(BlockUnitCount::try_from(invalid_count), Err(exp_err));
assert_eq!(BlockUnitCount::try_from(invalid_bytes), Err(exp_err));
assert_eq!(BlockUnitCount::try_from(&invalid_bytes), Err(exp_err));
});
}
}