sdmmc-core 0.5.0

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

pub const PRODUCT_SN_LEN: usize = 4;

/// Represents the product serial number as a 32-bit number.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProductSerialNumber(u32);

impl ProductSerialNumber {
    /// Creates a new [ProductSerialNumber].
    pub const fn new() -> Self {
        Self(0)
    }

    /// Gets the bit value of the [ProductSerialNumber].
    pub const fn bits(&self) -> u32 {
        self.0
    }

    /// Converts a [`u32`] into a [ProductSerialNumber].
    pub const fn from_bits(val: u32) -> Self {
        Self(val)
    }

    /// Converts a [ProductSerialNumber] into a byte array.
    pub const fn bytes(&self) -> [u8; PRODUCT_SN_LEN] {
        self.0.to_be_bytes()
    }

    /// Attempts to convert a byte slice into a [ProductSerialNumber].
    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
        match val.len() {
            len if len < PRODUCT_SN_LEN => Err(Error::InvalidVariant(len)),
            _ => Ok(Self(u32::from_be_bytes([val[0], val[1], val[2], val[3]]))),
        }
    }
}

impl Default for ProductSerialNumber {
    fn default() -> Self {
        Self::new()
    }
}

impl From<ProductSerialNumber> for u32 {
    fn from(val: ProductSerialNumber) -> Self {
        val.bits()
    }
}

impl From<ProductSerialNumber> for [u8; PRODUCT_SN_LEN] {
    fn from(val: ProductSerialNumber) -> Self {
        val.bytes()
    }
}

impl TryFrom<&[u8]> for ProductSerialNumber {
    type Error = Error;

    fn try_from(val: &[u8]) -> Result<Self> {
        Self::try_from_bytes(val)
    }
}

impl<const N: usize> TryFrom<[u8; N]> for ProductSerialNumber {
    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 ProductSerialNumber {
    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_valid() {
        (0..1024).chain([u32::MAX]).for_each(|raw| {
            let raw_bytes = raw.to_be_bytes();
            let exp_psn = ProductSerialNumber(raw);

            assert_eq!(ProductSerialNumber::from_bits(raw), exp_psn);
            assert_eq!(exp_psn.bits(), raw);
            assert_eq!(exp_psn.bytes(), raw_bytes);

            assert_eq!(
                ProductSerialNumber::try_from_bytes(raw_bytes.as_ref()),
                Ok(exp_psn)
            );
            assert_eq!(
                ProductSerialNumber::try_from(raw_bytes.as_ref()),
                Ok(exp_psn)
            );
            assert_eq!(ProductSerialNumber::try_from(raw_bytes), Ok(exp_psn));
            assert_eq!(ProductSerialNumber::try_from(&raw_bytes), Ok(exp_psn));

            assert_eq!(u32::from(exp_psn), raw);
            assert_eq!(<[u8; PRODUCT_SN_LEN]>::from(exp_psn), raw_bytes);
        });
    }

    #[test]
    fn test_invalid() {
        let raw = [0; PRODUCT_SN_LEN];

        (0..PRODUCT_SN_LEN).for_each(|len| {
            assert_eq!(
                ProductSerialNumber::try_from_bytes(&raw[..len]),
                Err(Error::InvalidVariant(len))
            );
        });
    }
}