sdmmc-core 0.5.0

SD/MMC core data structures and algorithms
Documentation
use crate::lib_bitfield;
use crate::result::{Error, Result};

lib_bitfield! {
    /// Represents the `buc` field of the [Argument](super::Argument).
    pub BlockUnitCount(u16): u16 {
        raw_count: 8, 0;
    }
}

impl BlockUnitCount {
    /// Represents the byte length of the [BlockUnitCount].
    pub const LEN: usize = 2;
    /// Represents the default inner value of the [BlockUnitCount].
    pub const DEFAULT: u16 = 0;
    /// Represents the minimum block unit count.
    pub const MIN: u16 = 1;
    /// Represents the maximum block unit count.
    pub const MAX: u16 = 512;

    /// Creates a new [BlockUnitCount].
    pub const fn new() -> Self {
        Self(Self::DEFAULT)
    }

    /// Gets the block unit count.
    ///
    /// # Note
    ///
    /// The returned value is already adjusted for the semantic count: `raw_count + 1`.
    pub const fn count(&self) -> u16 {
        self.raw_count() + 1
    }

    /// Gets the block unit count.
    ///
    /// # Note
    ///
    /// The set value is automatically adjusted for the semantic count: `val - 1`.
    pub fn set_count(&mut self, val: u16) {
        self.set_raw_count(val.saturating_sub(1));
    }

    /// Converts a [`u16`] into a [BlockUnitCount].
    ///
    /// # Safety
    ///
    /// Caller is responsible for checking that `val` is in the valid range: [MIN](Self::MIN) to [MAX](Self::MAX).
    pub const fn from_bits_unchecked(val: u16) -> Self {
        Self(val)
    }

    /// Attempts to convert a [`u16`] into a [BlockUnitCount].
    ///
    /// # Note
    ///
    /// Converts the unadjusted value into the [BlockUnitCount].
    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)),
        }
    }

    /// Attempts to convert a byte slice into a [BlockUnitCount].
    ///
    /// # Note
    ///
    /// Converts the unadjusted value into the [BlockUnitCount].
    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]])),
        }
    }

    /// Converts the [BlockUnitCount] into a byte array.
    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));
            });
    }
}