xio_common 0.12.0

XIO commonly used functionality
Documentation
use crate::base::DataValueRaw;
use crate::{
    error, DataValueReaderExt, DataValueWriterExt, Endian,
    HasFixedCommandId, MessageExt, ReadExt, ReadFromPayload, Result,
    WithFixedPayloadLength, WriteExt,
};
use snafu::ensure;
use std::io::{Read, Write};

#[derive(Clone, Debug, PartialEq)]
pub struct Notification {
    pub tag: u8,
    pub values: Vec<DataValueRaw>,
}

impl Notification {
    const ADDITIONAL_FIXED_PAYLOAD_LENGTH: u16 =
        u8::FIXED_PAYLOAD_LENGTH + u16::FIXED_PAYLOAD_LENGTH;

    fn verify_payload_length(received: u16, required: u16) -> Result<()> {
        ensure!(
            required == received,
            error::InvalidPayloadLength {
                message_type: "measurement_data_notification",
                received: received as usize,
                required: required as usize
            }
        );
        Ok(())
    }
}

impl From<Notification> for super::Notification {
    fn from(m: Notification) -> Self {
        super::Notification::MeasurementData(m)
    }
}

impl HasFixedCommandId for Notification {
    const COMMAND_ID: u16 = 0x8805;
}

impl MessageExt for Notification {
    fn payload_length(&self) -> u16 {
        Self::ADDITIONAL_FIXED_PAYLOAD_LENGTH
            + self.values.len() as u16 * DataValueRaw::FIXED_PAYLOAD_LENGTH
    }

    fn write_payload(&self, w: &mut dyn Write) -> Result<()> {
        w.write_u8(self.tag)?;
        w.write_u16::<Endian>(self.values.len() as u16)?;
        w.write_data_values(&self.values)?;
        Ok(())
    }
}

impl ReadFromPayload for Notification {
    fn read_from_payload<R: Read>(
        r: &mut R,
        payload_length: u16,
    ) -> Result<Self> {
        let tag = r.read_u8()?;
        let count = r.read_u16::<Endian>()?;

        let required_payload_length = Self::ADDITIONAL_FIXED_PAYLOAD_LENGTH
            + count * DataValueRaw::FIXED_PAYLOAD_LENGTH;
        Self::verify_payload_length(
            payload_length,
            required_payload_length,
        )?;
        let values = r.read_data_values(count as usize)?;

        Ok(Self { tag, values })
    }
}

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

    #[test]
    fn write_to_empty() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let tag = 0x87;
        let values = Vec::new();

        let message = Notification { tag, values };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x05, // command lower byte
                0x88, // command upper byte
                0x03, // length lower byte
                0x00, // length upper byte
                0x87, // tag
                0x00, // length lower byte
                0x00, // length upper byte
            ]
        );
    }
    #[test]
    fn write_to_single_boolean() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let tag = 0x87;
        let values = vec![true.into()];

        let length = {
            let fixed = 3u16;
            let per_value = 9u16;
            let value_count = 1u16;
            fixed + per_value * value_count
        };
        assert_eq!(length, 0x000C);
        assert_eq!(0x000C, 12);

        let message = Notification { tag, values };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x05, // command lower byte
                0x88, // command upper byte
                0x0C, // length lower byte
                0x00, // length upper byte
                0x87, // tag
                0x01, // value count lower byte
                0x00, // value count upper byte
                0x00, // value 0 type
                0x01, // value 0 byte 0
                0x00, // value 0 byte 1
                0x00, // value 0 byte 2
                0x00, // value 0 byte 3
                0x00, // value 0 byte 4
                0x00, // value 0 byte 5
                0x00, // value 0 byte 6
                0x00, // value 0 byte 7
            ]
        );
    }
    #[test]
    fn write_to_three_items() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let tag = 0x87;
        let values = vec![
            0x1234u16.into(),
            true.into(),
            0x9988776655443322u64.into(),
        ];

        let length = {
            let fixed = 3u16;
            let per_value = 9u16;
            let value_count = 3u16;
            fixed + per_value * value_count
        };
        assert_eq!(length, 0x001E);
        assert_eq!(0x001E, 30);

        let message = Notification { tag, values };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x05, // command lower byte
                0x88, // command upper byte
                0x1E, // length lower byte
                0x00, // length upper byte
                0x87, // tag
                0x03, // value count lower byte
                0x00, // value count upper byte
                0x06, // value 0 type
                0x00, // value 1 type
                0x08, // value 2 type
                0x34, // value 0 byte 0
                0x12, // value 0 byte 1
                0x00, // value 0 byte 2
                0x00, // value 0 byte 3
                0x00, // value 0 byte 4
                0x00, // value 0 byte 5
                0x00, // value 0 byte 6
                0x00, // value 0 byte 7
                0x01, // value 1 byte 0
                0x00, // value 1 byte 1
                0x00, // value 1 byte 2
                0x00, // value 1 byte 3
                0x00, // value 1 byte 4
                0x00, // value 1 byte 5
                0x00, // value 1 byte 6
                0x00, // value 1 byte 7
                0x22, // value 2 byte 0
                0x33, // value 2 byte 1
                0x44, // value 2 byte 2
                0x55, // value 2 byte 3
                0x66, // value 2 byte 4
                0x77, // value 2 byte 5
                0x88, // value 2 byte 6
                0x99, // value 2 byte 7
            ]
        );
    }
    #[test]
    fn from_payload_empty() {
        let buffer = vec![
            0x86, // tag
            0x00, // length lower byte
            0x00, // length upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Notification::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let tag = 0x86;
        let values = Vec::new();

        assert_eq!(message, Notification { tag, values });
    }
    #[test]
    fn from_payload_single_bool() {
        let buffer = vec![
            0x86, // tag
            0x01, // value count lower byte
            0x00, // value count upper byte
            0x00, // value 0 type
            0x01, // value 0 byte 0
            0x00, // value 0 byte 1
            0x00, // value 0 byte 2
            0x00, // value 0 byte 3
            0x00, // value 0 byte 4
            0x00, // value 0 byte 5
            0x00, // value 0 byte 6
            0x00, // value 0 byte 7
        ];
        let len = buffer.len() as u16;
        let message =
            Notification::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let tag = 0x86;
        let values = vec![true.into()];

        assert_eq!(message, Notification { tag, values });
    }
    #[test]
    fn read_from_three_items() {
        let buffer = vec![
            0x87, // tag
            0x03, // value count lower byte
            0x00, // value count upper byte
            0x06, // value 0 type
            0x00, // value 1 type
            0x08, // value 2 type
            0x34, // value 0 byte 0
            0x12, // value 0 byte 1
            0x00, // value 0 byte 2
            0x00, // value 0 byte 3
            0x00, // value 0 byte 4
            0x00, // value 0 byte 5
            0x00, // value 0 byte 6
            0x00, // value 0 byte 7
            0x01, // value 1 byte 0
            0x00, // value 1 byte 1
            0x00, // value 1 byte 2
            0x00, // value 1 byte 3
            0x00, // value 1 byte 4
            0x00, // value 1 byte 5
            0x00, // value 1 byte 6
            0x00, // value 1 byte 7
            0x22, // value 2 byte 0
            0x33, // value 2 byte 1
            0x44, // value 2 byte 2
            0x55, // value 2 byte 3
            0x66, // value 2 byte 4
            0x77, // value 2 byte 5
            0x88, // value 2 byte 6
            0x99, // value 2 byte 7
        ];
        let len = buffer.len() as u16;
        let message =
            Notification::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let tag = 0x87;
        let values = vec![
            0x1234u16.into(),
            true.into(),
            0x9988776655443322u64.into(),
        ];

        assert_eq!(message, Notification { tag, values });
    }
}