xio_common 0.12.0

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

#[derive(Clone, Debug, PartialEq)]
pub struct Request {
    pub job_slot: u16,
}

impl IsRequest for Request {
    type Response = Response;
}

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

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

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

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

    fn write_payload(&self, w: &mut dyn Write) -> Result<()> {
        w.write_u16::<Endian>(self.job_slot)?;
        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_job_request")?;
        let job_slot = r.read_u16::<Endian>()?;
        Ok(Self { job_slot })
    }
}

#[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 { job_slot: 0x4422 };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            [
                0x45, // options
                0x93, // sequence
                0x0a, // command lower byte
                0x28, // command upper byte
                0x02, // length lower byte
                0x00, // length upper byte
                0x22, // job slot lower byte
                0x44, // job slot upper byte
            ]
        );
    }
    #[test]
    fn read_from_payload() {
        let buffer = vec![
            0x33, // job slot lower byte
            0x55, // job slot upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Request::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();
        assert_eq!(message, Request { job_slot: 0x5533 });
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Response {
    pub error: ErrorCode,
    pub commands: Vec<u16>,
}

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::QueryJob(r) = r {
            Ok(r)
        } else {
            error::UnexpectedResponseType.fail()?
        }
    }
}

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

impl Response {
    const ADDITIONAL_FIXED_PAYLOAD_LENGTH: u16 =
        ErrorCode::FIXED_PAYLOAD_LENGTH + u16::FIXED_PAYLOAD_LENGTH;
    const JOB_COMMAND_LENGTH: u16 = u16::FIXED_PAYLOAD_LENGTH;

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

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

impl MessageExt for Response {
    fn payload_length(&self) -> u16 {
        Self::ADDITIONAL_FIXED_PAYLOAD_LENGTH
            + Self::JOB_COMMAND_LENGTH * self.commands.len() as u16
    }

    fn write_payload(&self, w: &mut dyn Write) -> Result<()> {
        w.write_error_code(self.error)?;
        w.write_u16::<Endian>(self.commands.len() as u16)?;
        for value in &self.commands {
            w.write_u16::<Endian>(*value)?;
        }
        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 count = r.read_u16::<Endian>()?;
        let required_payload_length = Self::ADDITIONAL_FIXED_PAYLOAD_LENGTH
            + count * Self::JOB_COMMAND_LENGTH;

        Self::verify_payload_length(
            payload_length,
            required_payload_length,
        )?;
        let mut commands = Vec::new();
        for _ in 0..count {
            commands.push(r.read_u16::<Endian>()?);
        }
        Ok(Self { error, commands })
    }
}

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

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

        let commands = Vec::new();

        let message = Response {
            error: ErrorCode::InvalidState, // 0x0107
            commands,
        };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            [
                0x45, // options
                0x93, // sequence
                0x0b, // command lower byte
                0x28, // command upper byte
                0x04, // length lower byte
                0x00, // length upper byte
                0x07, // error lower byte
                0x01, // error upper byte
                0x00, // value count lower byte
                0x00, // value count upper byte
            ]
        );
    }
    #[test]
    fn write_to_single_item() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let error = ErrorCode::InvalidState;
        let commands = vec![0x1234];

        let length = {
            let fixed = 4u16;
            let per_value = 2u16;
            let value_count = 1u16;
            fixed + per_value * value_count
        };
        assert_eq!(length, 0x0006);

        let message = Response { error, commands };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x0b, // command lower byte
                0x28, // command upper byte
                0x06, // length lower byte
                0x00, // length upper byte
                0x07, // error lower byte
                0x01, // error upper byte
                0x01, // value count lower byte
                0x00, // value count upper byte
                0x34, // command 0 lower byte
                0x12, // command 0 upper byte
            ]
        );
    }
    #[test]
    fn write_to_three_items() {
        let mut buffer = Vec::new();
        let options: u8 = 0x45;
        let sequence: u8 = 0x93;

        let error = ErrorCode::InvalidState;
        let commands = vec![0x9988, 0x7766, 0x5544];

        let length = {
            let fixed = 4u16;
            let per_value = 2u16;
            let value_count = 3u16;
            fixed + per_value * value_count
        };
        assert_eq!(length, 0x000A);
        assert_eq!(0x000A, 10);

        let message = Response { error, commands };
        message.write_to(&mut buffer, options, sequence).unwrap();
        assert_eq!(
            buffer,
            vec![
                0x45, // options
                0x93, // sequence
                0x0b, // command lower byte
                0x28, // command upper byte
                0x0A, // length lower byte
                0x00, // length upper byte
                0x07, // error lower byte
                0x01, // error upper byte
                0x03, // value count lower byte
                0x00, // value count upper byte
                0x88, // command 0 lower byte
                0x99, // command 0 upper byte
                0x66, // command 1 lower byte
                0x77, // command 1 upper byte
                0x44, // command 2 lower byte
                0x55, // command 2 upper byte
            ]
        );
    }
    #[test]
    fn from_payload_empty() {
        let buffer = vec![
            0x07, // error lower byte
            0x01, // error upper byte
            0x00, // value count lower byte
            0x00, // value count upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Response::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();
        let commands = Vec::new();
        assert_eq!(
            message,
            Response {
                error: ErrorCode::InvalidState,
                commands,
            }
        );
    }
    #[test]
    fn from_payload_single_item() {
        let buffer = vec![
            0x07, // error lower byte
            0x01, // error upper byte
            0x01, // value count lower byte
            0x00, // value count upper byte
            0x88, // command 0 lower byte
            0x99, // command 0 upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Response::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let error = ErrorCode::InvalidState;
        let commands = vec![0x9988];

        assert_eq!(message, Response { error, commands });
    }
    #[test]
    fn read_from_three_items() {
        let buffer = vec![
            0x07, // error lower byte
            0x01, // error upper byte
            0x03, // value count lower byte
            0x00, // value count upper byte
            0x88, // command 0 lower byte
            0x99, // command 0 upper byte
            0x66, // command 1 lower byte
            0x77, // command 1 upper byte
            0x44, // command 2 lower byte
            0x55, // command 2 upper byte
        ];
        let len = buffer.len() as u16;
        let message =
            Response::read_from_payload(&mut buffer.as_slice(), len)
                .unwrap();

        let error = ErrorCode::InvalidState;
        let commands = vec![0x9988, 0x7766, 0x5544];

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