xio_common 0.12.0

XIO commonly used functionality
Documentation
use crate::base::{ErrorCode, HasErrorCode};
use crate::{
    error, ErrorCodeReaderExt, ErrorCodeWriterExt, HasFixedCommandId,
    IsRequest, IsResponse, IsValidResponseFor, MessageExt,
    ReadFromPayload, Result, TryFromResponse, WithFixedPayloadLength,
    WriteExt,
};
use snafu::ResultExt;
use std::io::{Read, Write};

#[derive(Clone, Debug, PartialEq)]
pub struct Request;

impl IsRequest for Request {
    type Response = Response;
}

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

impl WithFixedPayloadLength for Request {
    const FIXED_PAYLOAD_LENGTH: u16 = 0u16;
}

impl HasFixedCommandId for Request {
    const COMMAND_ID: u16 = 0x0800;
}

impl MessageExt for Request {
    fn payload_length(&self) -> u16 {
        Self::FIXED_PAYLOAD_LENGTH
    }

    fn write_payload(&self, _w: &mut dyn Write) -> Result<()> {
        Ok(())
    }
}

impl ReadFromPayload for Request {
    fn read_from_payload<R: Read>(
        _r: &mut R,
        payload_length: u16,
    ) -> Result<Self> {
        Self::verify_payload_length(
            payload_length,
            "query_device_firmware_version_request",
        )?;
        Ok(Self {})
    }
}

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

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

        let message = Request {};
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            [
                0x45, // options
                0x93, // sequence
                0x00, // command lower byte
                0x08, // command upper byte
                0x00, // length lower byte
                0x00, // length upper byte
            ]
        );
    }
    #[test]
    fn read_from_payload() {
        let buffer = vec![];
        let len = buffer.len() as u16;
        let message =
            Request::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();
        assert_eq!(message, Request {});
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Response {
    pub error: ErrorCode,
    pub version: String,
}

impl IsResponse for Response {
    type Request = Request;
}

impl IsValidResponseFor for Response {
    fn is_valid_response_for(&self, _request: &Request) -> bool {
        true
    }
}

impl HasErrorCode for Response {
    fn error_code(&self) -> ErrorCode {
        self.error
    }
}

impl TryFromResponse for Response {
    fn try_from_response(r: super::Response) -> Result<Self> {
        if let super::Response::QueryDeviceFirmwareVersion(r) = r {
            Ok(r)
        } else {
            error::UnexpectedResponseType.fail()
        }
    }
}

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

impl HasFixedCommandId for Response {
    const COMMAND_ID: u16 = 0x0801;
}

impl MessageExt for Response {
    fn payload_length(&self) -> u16 {
        ErrorCode::FIXED_PAYLOAD_LENGTH
            + self.version.as_bytes().len() as u16
    }

    fn write_payload(&self, w: &mut dyn Write) -> Result<()> {
        w.write_error_code(self.error)?;
        w.write_all_(self.version.as_bytes())?;
        Ok(())
    }
}

impl ReadFromPayload for Response {
    fn read_from_payload<R: Read>(
        r: &mut R,
        payload_length: u16,
    ) -> Result<Self> {
        let error = r.read_error_code()?;
        let mut version = vec![
            0u8;
            (payload_length - ErrorCode::FIXED_PAYLOAD_LENGTH)
                as usize
        ];
        r.read_exact(&mut version).context(error::Io)?;
        let version =
            String::from_utf8(version).context(error::Utf8Conversion)?;
        Ok(Self { error, version })
    }
}

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

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

        let error = ErrorCode::Success;
        let version = "".to_string();

        let message = Response { error, version };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x01, // command lower byte
                0x08, // command upper byte
                0x02, // length lower byte
                0x00, // length upper byte
                0x00, // error lower byte
                0x00, // error upper byte
            ]
        );
    }
    #[test]
    fn write_to_name() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let error = ErrorCode::Success;
        let version = "Virtual 3.2".to_string();

        let message = Response { error, version };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x01, // command lower byte
                0x08, // command upper byte
                0x0D, // length lower byte
                0x00, // length upper byte
                0x00, // error lower byte
                0x00, // error upper byte
                0x56, // 'V'
                0x69, // 'i'
                0x72, // 'r'
                0x74, // 't'
                0x75, // 'u'
                0x61, // 'a'
                0x6C, // 'l'
                0x20, // ' '
                0x33, // '3'
                0x2E, // '.'
                0x32, // '2'
            ]
        );
    }
    #[test]
    fn from_payload_empty() {
        let buffer = vec![
            0x00, // error lower byte
            0x00, // error upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Response::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let error = ErrorCode::Success;
        let version = "".to_string();

        assert_eq!(message, Response { error, version });
    }
    #[test]
    fn from_payload_name() {
        let buffer = vec![
            0x00, // error lower byte
            0x00, // error upper byte
            0x56, // 'V'
            0x69, // 'i'
            0x72, // 'r'
            0x74, // 't'
            0x75, // 'u'
            0x61, // 'a'
            0x6C, // 'l'
            0x20, // ' '
            0x33, // '3'
            0x2E, // '.'
            0x32, // '2'
        ];
        let len = buffer.len() as u16;
        let message =
            Response::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let error = ErrorCode::Success;
        let version = "Virtual 3.2".to_string();

        assert_eq!(message, Response { error, version });
    }
}