nimbi-protocol 0.1.0

A crate for defining the nimbi-protocol used to communicate with microcontrollers
Documentation
use microdot::{deserialize::Deserialize, serialize::Serialize, MicrodotError};

pub const ID_SIZE: usize = 6;

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub struct Id<'a>(&'a [u8; ID_SIZE]);

impl<'a> Id<'a> {
    pub fn from_str_unchecked(s: &'a str) -> Self {
        let id = s.as_bytes()[..6].try_into().unwrap();

        Id(id)
    }
}

impl<'a> TryFrom<&'a str> for Id<'a> {
    type Error = ParseIdError;

    fn try_from(s: &'a str) -> Result<Id<'a>, Self::Error> {
        let bytes = s.as_bytes();

        if bytes.len() != 6 {
            return Err(ParseIdError::IvalidLength);
        }

        let id = bytes.try_into().map_err(|_| ParseIdError::TryFromSlice)?;

        Ok(Id(id))
    }
}

#[derive(Debug)]
pub enum ParseIdError {
    IvalidLength,
    TryFromSlice,
}

impl Serialize for Id<'_> {
    fn serialize(&self, buf: &mut [u8]) -> Result<usize, MicrodotError> {
        let bytes = &self.0[..];

        bytes.serialize(buf)
    }
}

impl<'a> Deserialize<'a> for Id<'a> {
    fn deserialize(buf: &'a [u8]) -> Result<Id<'a>, MicrodotError> {
        if buf.len() < ID_SIZE {
            return Err(microdot::error::Error::invalid_length());
        }

        let id = buf[..6].try_into()?;

        Ok(Self(id))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn id_should_serialize() {
        let id = Id(&[1, 1, 1, 1, 1, 1]);

        let mut buf = [0u8; 6];

        match id.serialize(&mut buf) {
            Ok(bytes_written) => {
                assert_eq!(bytes_written, 6);
                assert_eq!(&buf[..], &[1, 1, 1, 1, 1, 1]);
            }
            Err(err) => {
                dbg!(err);

                unreachable!();
            }
        }
    }
}