sen6x-driver 0.0.3

embedded-hal driver for the sensirion sen6x sensors
use crate::Error;
use sensirion_i2c::crc8::calculate;

pub(crate) trait FromBytes<const N: usize, R> {
    fn from_bytes_with_crc<E>(bytes: &[u8; N]) -> Result<R, Error<E>>;
}

pub(crate) const fn get_size<O, F, const N: usize>(_: fn(x: O) -> F) -> usize
where
    F: FromBytes<N, F>,
{
    N
}

impl FromBytes<2, u16> for u16 {
    fn from_bytes_with_crc<E>(bytes: &[u8; 2]) -> Result<u16, Error<E>> {
        Ok(u16::from_be_bytes(*bytes))
    }
}
impl FromBytes<2, i16> for i16 {
    fn from_bytes_with_crc<E>(bytes: &[u8; 2]) -> Result<i16, Error<E>> {
        Ok(i16::from_be_bytes(*bytes))
    }
}

impl FromBytes<4, u32> for u32 {
    fn from_bytes_with_crc<E>(bytes: &[u8; 4]) -> Result<u32, Error<E>> {
        Ok(u32::from_be_bytes(*bytes))
    }
}

impl FromBytes<1, u8> for u8 {
    fn from_bytes_with_crc<E>(bytes: &[u8; 1]) -> Result<u8, Error<E>> {
        Ok(bytes[0])
    }
}

impl FromBytes<2, Option<i16>> for Option<i16> {
    fn from_bytes_with_crc<E>(bytes: &[u8; 2]) -> Result<Option<i16>, Error<E>> {
        let res = i16::from_bytes_with_crc(bytes)?;
        if res == i16::MAX {
            return Ok(None);
        }
        Ok(Some(res))
    }
}

impl FromBytes<2, Option<u16>> for Option<u16> {
    fn from_bytes_with_crc<E>(bytes: &[u8; 2]) -> Result<Option<u16>, Error<E>> {
        let res = u16::from_bytes_with_crc(bytes)?;
        if res == u16::MAX {
            return Ok(None);
        }
        Ok(Some(res))
    }
}

impl FromBytes<2, bool> for bool {
    fn from_bytes_with_crc<E>(bytes: &[u8; 2]) -> Result<bool, Error<E>> {
        match bytes[1] {
            0x00 => Ok(false),
            0x01 => Ok(true),
            _ => Err(Error::InvalidValue),
        }
    }
}

pub(super) trait ToBytes<const N: usize> {
    fn to_bytes(&self) -> [u8; N];
}

impl ToBytes<2> for bool {
    fn to_bytes(&self) -> [u8; 2] {
        [0x00, *self as u8]
    }
}
impl ToBytes<2> for u16 {
    fn to_bytes(&self) -> [u8; 2] {
        self.to_be_bytes()
    }
}

impl ToBytes<2> for i16 {
    fn to_bytes(&self) -> [u8; 2] {
        self.to_be_bytes()
    }
}

impl ToBytes<2> for Option<i16> {
    fn to_bytes(&self) -> [u8; 2] {
        self.unwrap_or(0x7FFF).to_be_bytes()
    }
}

impl ToBytes<2> for Option<u16> {
    fn to_bytes(&self) -> [u8; 2] {
        self.unwrap_or(0xFFFF).to_be_bytes()
    }
}

impl ToBytes<1> for u8 {
    fn to_bytes(&self) -> [u8; 1] {
        [*self]
    }
}

impl<const N: usize> ToBytes<N> for [u8; N] {
    fn to_bytes(&self) -> [u8; N] {
        *self
    }
}

pub(crate) fn check_crc<const N: usize, E>(bytes: &[u8]) -> Result<[u8; N], Error<E>> {
    assert_eq!(N % 2, 0);
    assert_eq!(bytes.len() * 2 / 3, N);
    let mut dest = [0u8; N];
    for i in 0..N / 2 {
        if calculate(&bytes[i * 3..i * 3 + 2]) != bytes[i * 3 + 2] {
            return Err(Error::Crc);
        }
        for j in 0..2 {
            dest[i * 2 + j] = bytes[i * 3 + j];
        }
    }
    Ok(dest)
}

pub(crate) trait ValueWrapper {
    type Inner;
    fn wrap(value: Self::Inner) -> Self;
    fn unwrap(&self) -> Self::Inner;
}

impl<const N: usize, W> FromBytes<N, W> for W
where
    W: ValueWrapper,
    W::Inner: FromBytes<N, W::Inner>,
{
    fn from_bytes_with_crc<E>(bytes: &[u8; N]) -> Result<W, Error<E>> {
        W::Inner::from_bytes_with_crc(bytes).map(W::wrap)
    }
}

impl<const N: usize, W> FromBytes<N, Option<W>> for Option<W>
where
    W: ValueWrapper,
    Option<W::Inner>: FromBytes<N, Option<W::Inner>>,
{
    fn from_bytes_with_crc<E>(bytes: &[u8; N]) -> Result<Option<W>, Error<E>> {
        Option::<W::Inner>::from_bytes_with_crc(bytes).map(|v| v.map(W::wrap))
    }
}

impl<const N: usize, W> ToBytes<N> for W
where
    W: ValueWrapper,
    W::Inner: ToBytes<N>,
{
    fn to_bytes(&self) -> [u8; N] {
        self.unwrap().to_bytes()
    }
}

impl<const N: usize, W> ToBytes<N> for Option<W>
where
    W: ValueWrapper,
    Option<W::Inner>: ToBytes<N>,
{
    fn to_bytes(&self) -> [u8; N] {
        self.as_ref().map(|v| v.unwrap()).to_bytes()
    }
}