wayk_proto 0.1.0

WaykNow packet encoder-decoder and sequencing utilities
Documentation
// NOW_ASSOCIATE_MSG

use crate::message::status::{AssociateStatusCode, NowStatus};
use num_derive::FromPrimitive;

#[derive(Decode, Encode, FromPrimitive, Debug, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum AssociateMessageType {
    Info = 0x01,
    Request = 0x02,
    Response = 0x03,
}

#[derive(Debug, Clone, Encode, Decode)]
#[meta_enum = "AssociateMessageType"]
pub enum NowAssociateMsg {
    Info(NowAssociateInfoMsg),
    Request(NowAssociateRequestMsg),
    Response(NowAssociateResponseMsg),
}

impl NowAssociateMsg {
    pub fn new_info() -> Self {
        Self::Info(NowAssociateInfoMsg::default())
    }

    pub fn new_request() -> Self {
        Self::Request(NowAssociateRequestMsg::default())
    }

    pub fn new_response() -> Self {
        Self::Response(NowAssociateResponseMsg::default())
    }

    pub fn new_response_with_status(status: NowStatus<AssociateStatusCode>) -> Self {
        Self::Response(NowAssociateResponseMsg {
            status,
            ..NowAssociateResponseMsg::default()
        })
    }
}

impl From<NowAssociateInfoMsg> for NowAssociateMsg {
    fn from(msg: NowAssociateInfoMsg) -> Self {
        Self::Info(msg)
    }
}

impl From<NowAssociateRequestMsg> for NowAssociateMsg {
    fn from(msg: NowAssociateRequestMsg) -> Self {
        Self::Request(msg)
    }
}

impl From<NowAssociateResponseMsg> for NowAssociateMsg {
    fn from(msg: NowAssociateResponseMsg) -> Self {
        Self::Response(msg)
    }
}

// subtypes

__flags_struct! {
    AssociateInfoFlags: u16 => {
        active = ACTIVE = 0x0001,
        failure = FAILURE = 0x8000,
    }
}

#[derive(Decode, Encode, Debug, Clone)]
pub struct NowAssociateInfoMsg {
    subtype: AssociateMessageType,
    reserved: u8,
    pub flags: AssociateInfoFlags,
    pub session_id: u32,
}

impl Default for NowAssociateInfoMsg {
    fn default() -> Self {
        Self::new(AssociateInfoFlags::new_empty())
    }
}

impl NowAssociateInfoMsg {
    pub const SUBTYPE: AssociateMessageType = AssociateMessageType::Info;

    pub fn new(flags: AssociateInfoFlags) -> Self {
        Self::new_with_session_id(flags, 0x0000_0000)
    }

    pub fn new_with_session_id(flags: AssociateInfoFlags, session_id: u32) -> Self {
        Self {
            subtype: Self::SUBTYPE,
            reserved: 0,
            flags,
            session_id,
        }
    }
}

__flags_struct! {
    AssociateRequestFlags: u16 => {
        force = FORCE = 0x0001,
        failure = FAILURE = 0x8000,
    }
}

#[derive(Encode, Decode, Debug, Clone)]
pub struct NowAssociateRequestMsg {
    subtype: AssociateMessageType,
    reserved: u8,
    pub flags: AssociateRequestFlags,
    pub session_id: u32,
}

impl Default for NowAssociateRequestMsg {
    fn default() -> Self {
        Self::new(AssociateRequestFlags::new_empty())
    }
}

impl NowAssociateRequestMsg {
    pub const SUBTYPE: AssociateMessageType = AssociateMessageType::Request;

    pub fn new(flags: AssociateRequestFlags) -> Self {
        Self::new_with_session_id(flags, 0x0000_0000)
    }

    pub fn new_with_session_id(flags: AssociateRequestFlags, session_id: u32) -> Self {
        Self {
            subtype: Self::SUBTYPE,
            reserved: 0x00,
            flags,
            session_id,
        }
    }
}

__flags_struct! {
    AssociateResponseFlags: u16 => {
        failure = FAILURE = 0x8000,
    }
}

#[derive(Decode, Encode, Debug, Clone)]
pub struct NowAssociateResponseMsg {
    subtype: AssociateMessageType,
    reserved: u8,
    pub flags: AssociateResponseFlags,
    pub session_id: u32,
    pub status: NowStatus<AssociateStatusCode>,
}

impl Default for NowAssociateResponseMsg {
    fn default() -> Self {
        Self::new(AssociateResponseFlags::new_empty(), NowStatus::default())
    }
}

impl NowAssociateResponseMsg {
    pub const SUBTYPE: AssociateMessageType = AssociateMessageType::Response;

    pub fn new(flags: AssociateResponseFlags, status: NowStatus<AssociateStatusCode>) -> Self {
        Self::new_with_session_id(flags, status, 0x0000_0000)
    }

    pub fn new_with_session_id(
        flags: AssociateResponseFlags,
        status: NowStatus<AssociateStatusCode>,
        session_id: u32,
    ) -> Self {
        Self {
            subtype: Self::SUBTYPE,
            reserved: 0x00,
            flags,
            session_id,
            status,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::serialization::{Decode, Encode};

    #[test]
    fn decoding_with_subtype_check() {
        let msg = NowAssociateMsg::decode(&ASSOCIATE_MSG_REQUEST).unwrap();
        if let NowAssociateMsg::Request(msg) = msg {
            assert_eq!(msg.subtype, AssociateMessageType::Request);
            assert_eq!(msg.reserved, 0x00);
            assert_eq!(msg.flags, 0x0000);
            assert_eq!(msg.session_id, 0x0000_0000);
        } else {
            panic!("Expected a request message, found {:?}", msg);
        }
    }

    const ASSOCIATE_MSG_INFO: [u8; 8] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    #[test]
    fn info_decoding() {
        let msg = NowAssociateInfoMsg::decode(&ASSOCIATE_MSG_INFO).unwrap();
        assert_eq!(msg.subtype, AssociateMessageType::Info);
        assert_eq!(msg.reserved, 0x00);
        assert_eq!(msg.flags, 0x0000);
        assert_eq!(msg.session_id, 0x0000_0000);
    }

    #[test]
    fn associate_info_encoding() {
        let request = NowAssociateMsg::new_info();
        assert_eq!(ASSOCIATE_MSG_INFO, request.encode().unwrap()[0..]);
    }

    const ASSOCIATE_MSG_REQUEST: [u8; 8] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    #[test]
    fn request_decoding() {
        let msg = NowAssociateInfoMsg::decode(&ASSOCIATE_MSG_REQUEST).unwrap();
        assert_eq!(msg.subtype, AssociateMessageType::Request);
        assert_eq!(msg.reserved, 0x00);
        assert_eq!(msg.flags, 0x0000);
        assert_eq!(msg.session_id, 0x0000_0000);
    }

    #[test]
    fn request_encoding() {
        let request = NowAssociateMsg::new_request();
        assert_eq!(ASSOCIATE_MSG_REQUEST, request.encode().unwrap()[0..]);
    }

    const ASSOCIATE_MSG_RESPONSE: [u8; 12] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    #[test]
    fn response_decoding() {
        let msg = NowAssociateResponseMsg::decode(&ASSOCIATE_MSG_RESPONSE).unwrap();
        assert_eq!(msg.subtype, AssociateMessageType::Response);
        assert_eq!(msg.reserved, 0x00);
        assert_eq!(msg.flags, 0x0000);
        assert_eq!(msg.session_id, 0x0000_0000);
        assert_eq!(msg.status, 0x0000_0000);
    }

    #[test]
    fn response_encoding() {
        let request = NowAssociateMsg::new_response();
        assert_eq!(ASSOCIATE_MSG_RESPONSE, request.encode().unwrap()[0..]);
    }
}