ygopro-data 0.1.1

Data models for the YGOPro protocol: cards, messages and replay structures.
Documentation
//! Message traits and shared types.
//!
//! Provides the [`PureMessage`] / [`Message`] traits, the [`HostInfo`] struct, and the
//! message [`Error`] type.

use std::fmt::Debug;
use binrw::BinRead;
use binrw::BinWrite;

use crate::constants::MasterRule;
use crate::constants::Rule;

pub trait PureMessage: 'static {}

pub trait Message: PureMessage + Debug {
    fn message_type() -> crate::message::all::MessageType where Self: Sized;
}

#[derive(BinRead, BinWrite, Clone, Debug)]
#[repr(C)]
pub struct HostInfo {
    pub lflist: u32,
    pub rule: Rule,
    pub mode: crate::constants::Mode,
    pub duel_rule: crate::constants::MasterRule,
    #[br(map=|v:u8| v>0)]
    #[bw(map=|v| if *v {1u8} else {0u8})]
    pub no_check_deck: bool,
    #[br(map=|v:u8| v>0)]
    #[bw(map=|v| if *v {1u8} else {0u8})]
    #[brw(pad_after=3)]
    pub no_shuffle_deck: bool,
    pub start_lp: u32,
    pub start_hand: u8,
    pub draw_count: u8,
    pub time_limit: u16
}

impl Default for HostInfo {
    fn default() -> Self {
        Self { 
            lflist: 0, 
            rule: Rule::OCG,
            mode: crate::constants::Mode::Match, 
            duel_rule: MasterRule::MasterRule2020,
            no_check_deck: false, 
            no_shuffle_deck: false, 
            start_lp: 8000,
            start_hand: 5, 
            draw_count: 1, 
            time_limit: 180
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Something wrong when io")]
    IO(std::io::Error),
    #[error("Custom error")]
    Custom(String),
    #[error("Try to serialize a component over its design size")]
    Oversize,
    #[error("Deserialize finished, but remain some bytes")]
    Remain(Vec<u8>),
    #[error("Try to deserialize a seq without limit.")]
    Unlimited,
    #[error("Some error happened when unwrap the writer.")]
    UnwrapWriter,
    #[error("Try to deserialize to a wrong type.")]
    WrongType,
    #[error("Try to deserialize an unknown type message.")]
    UnknownType,
    #[error("Try to change full message to wrong status.")]
    WrongStatus,
}

#[macro_export]
macro_rules! generate_enum {
    ($($message_name:ident=$message_flag:literal),*) => {
        #[derive(binrw::BinRead, binrw::BinWrite, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
        #[brw(repr=u8)]
        #[repr(u8)]
        pub enum MessageType {
            Unknown(u8),
            $($message_name=$message_flag),*
        }
        
        impl std::convert::From<u8> for MessageType {
            fn from(value: u8) -> Self {
                match value {
                    $($message_flag => Self::$message_name,)*
                    _ => Self::Unknown(value)
                }
            }
        }

        impl std::convert::From<MessageType> for u8 {
            fn from(value: MessageType) -> Self {
                match value {
                    $(MessageType::$message_name => $message_flag,)*
                    MessageType::Unknown(v) => v
                }
            }
        }

        impl std::convert::From<&MessageType> for u8 {
            fn from(value: &MessageType) -> Self {
                match *value {
                    $(MessageType::$message_name => $message_flag,)*
                    MessageType::Unknown(v) => v
                }
            }
        }

        #[derive(binrw::BinRead, binrw::BinWrite, Clone, Debug)]
        pub enum Message {
            $(#[brw(magic($message_flag))]
            $message_name($message_name)),*
        }

        impl crate::message::PureMessage for Message {}

        impl From<&Message> for MessageType {
            fn from(value: &Message) -> Self {
                match value {
                    $(Message::$message_name(_) => MessageType::$message_name),*
                }
            }
        }

        $(
            impl TryFrom<Message> for $message_name {
                type Error = crate::message::Error;

                fn try_from(value: Message) -> Result<Self, Self::Error> {
                    match value {
                        Message::$message_name(v) => Ok(v),
                        _ => Err(crate::message::Error::WrongType)
                    }
                }
            }

            impl<'m> TryFrom<&'m Message> for &'m $message_name {
                type Error = crate::message::Error;

                fn try_from(value: &'m Message) -> Result<Self, Self::Error> {
                    match value {
                        Message::$message_name(v) => Ok(v),
                        _ => Err(crate::message::Error::WrongType)
                    }
                }
            }
            
            impl From<$message_name> for Message {
                fn from(value: $message_name) -> Self {
                    Message::$message_name(value)
                }
            }

            impl $message_name {
                pub fn into_message(self) -> Message { 
                    self.into() 
                } 
            }
        )*
    };
}

mod test {
    #![allow(unused_imports)]

    use std::io::Cursor;
    use binrw::BinRead;
    use binrw::BinWrite;

    use crate::message::client_to_server::HandResult;
    use crate::message::client_to_server::JoinGame;
    use crate::message::client_to_server::MessageType;
    use crate::message::client_to_server::Message;
    
    #[test]
    fn test_message_type_basic() {
        let message_type = MessageType::CreateGame;
        let mut vec = Cursor::new(Vec::<u8>::new());
        message_type.write_le(&mut vec).unwrap();
        assert_eq!(vec.into_inner(), [17]);

        let mut vec = Cursor::new(vec![127]);
        let message_type = MessageType::read_le(&mut vec).unwrap();
        assert_eq!(message_type, MessageType::Unknown(127));
    }

    #[test]
    fn test_message_enum_basic() {
        let message_enum = Message::HandResult(HandResult {
            res: crate::constants::Hand::Paper
        });
        let mut vec = Cursor::new(Vec::<u8>::new());
        message_enum.write_le(&mut vec).unwrap(); 
        println!("{:?}", vec.into_inner());
    }
}