Use a derive macro for service message serialization

This commit is contained in:
lcdr
2020-06-17 20:39:28 +02:00
parent 888c70dcf1
commit 1e4987d65a
5 changed files with 81 additions and 69 deletions
+7 -1
View File
@@ -2,6 +2,7 @@ mod from_variants;
mod game_message;
mod gm_deserialize;
mod service_message_d;
mod service_message_s;
use proc_macro::TokenStream;
@@ -21,6 +22,11 @@ pub fn derive_gm_deserialize(input: TokenStream) -> TokenStream {
}
#[proc_macro_derive(ServiceMessageD)]
pub fn derive_service_message(input: TokenStream) -> TokenStream {
pub fn derive_service_message_d(input: TokenStream) -> TokenStream {
service_message_d::derive(input)
}
#[proc_macro_derive(ServiceMessageS)]
pub fn derive_service_message_s(input: TokenStream) -> TokenStream {
service_message_s::derive(input)
}
@@ -0,0 +1,68 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, parse_quote, Data, DataEnum, DeriveInput, Fields, Generics};
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let data = match &input.data {
Data::Enum(data) => data,
_ => unimplemented!(),
};
let name = &input.ident;
let ser_code = gen_ser_code_enum(data, &name, &input.generics);
let ser_impl = &mut input.generics.clone();
ser_impl.params.push(parse_quote!('__LIFETIME));
ser_impl.params.push(parse_quote!(__WRITER: ::std::io::Write));
let (ser_impl, _, _) = ser_impl.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
(quote! {
impl #ser_impl ::endio::Serialize<::endio::LE, __WRITER> for &'__LIFETIME #name #ty_generics #where_clause {
fn serialize(self, writer: &mut __WRITER) -> ::std::io::Result<()> {
#ser_code
}
}
}).into()
}
fn gen_ser_code_fields(fields: &Fields) -> TokenStream {
match fields {
Fields::Named(_) => unimplemented!(),
Fields::Unnamed(fields) => {
let mut index = String::from("a");
let mut pat = vec![];
let mut ser = vec![];
for _ in &fields.unnamed {
let ident = Ident::new(&index, Span::call_site());
pat.push(quote! { #ident, });
ser.push(quote! { ::endio::LEWrite::write(writer, #ident)?; });
index += "a";
}
quote! { ( #(#pat)* ) => { #(#ser)* } }
}
Fields::Unit => {
quote! { => {} }
}
}
}
fn gen_ser_code_enum(data: &DataEnum, name: &Ident, generics: &Generics) -> TokenStream {
let mut arms = vec![];
for f in &data.variants {
let ident = &f.ident;
let ser_fields = gen_ser_code_fields(&f.fields);
let expanded = quote! { #name::#ident #ser_fields };
arms.push(expanded);
}
quote! {
let disc = unsafe { *(self as *const #name #generics as *const u32) };
::endio::LEWrite::write(writer, disc)?;
::endio::LEWrite::write(writer, 0u8)?;
match self {
#(#arms)*
}
Ok(())
}
}
+2 -19
View File
@@ -2,7 +2,7 @@ use std::io::Result as Res;
use endio::{LEWrite, Serialize};
use endio::LittleEndian as LE;
use lu_packets_derive::FromVariants;
use lu_packets_derive::{FromVariants, ServiceMessageS};
use crate::common::{LuStr33, LuWStr33};
@@ -15,30 +15,13 @@ impl From<ClientMessage> for Message {
}
}
#[derive(Debug, FromVariants)]
#[derive(Debug, FromVariants, ServiceMessageS)]
#[non_exhaustive]
#[repr(u32)]
pub enum ClientMessage {
LoginResponse(LoginResponse),
}
impl<'a, W: LEWrite> Serialize<LE, W> for &'a ClientMessage
where u8: Serialize<LE, W>,
u32: Serialize<LE, W>,
&'a LoginResponse: Serialize<LE, W> {
fn serialize(self, writer: &mut W) -> Res<()> {
let disc = unsafe { *(self as *const ClientMessage as *const u32) };
writer.write(disc)?;
writer.write(0u8)?;
match self {
ClientMessage::LoginResponse(message) => {
writer.write(message)?;
}
}
Ok(())
}
}
#[derive(Debug)]
#[non_exhaustive]
#[repr(u8)]
+2 -22
View File
@@ -2,6 +2,7 @@ use std::io::Result as Res;
use endio::{LEWrite, Serialize};
use endio::LittleEndian as LE;
use lu_packets_derive::ServiceMessageS;
use crate::common::ServiceId;
@@ -26,7 +27,7 @@ impl<C> From<GeneralMessage> for Message<C> {
}
}
#[derive(Debug)]
#[derive(Debug, ServiceMessageS)]
#[non_exhaustive]
#[repr(u32)]
pub enum GeneralMessage {
@@ -46,27 +47,6 @@ impl<C> From<DisconnectNotify> for Message<C> {
}
}
impl<'a, W: LEWrite> Serialize<LE, W> for &'a GeneralMessage
where u8: Serialize<LE, W>,
u32: Serialize<LE, W>,
&'a Handshake: Serialize<LE, W>,
&'a DisconnectNotify: Serialize<LE, W> {
fn serialize(self, writer: &mut W) -> Res<()> {
let disc = unsafe { *(self as *const GeneralMessage as *const u32) };
writer.write(disc)?;
writer.write(0u8)?;
match self {
GeneralMessage::Handshake(msg) => {
writer.write(msg)?;
}
GeneralMessage::DisconnectNotify(msg) => {
writer.write(msg)?;
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct Handshake {
pub network_version: u32,
+2 -27
View File
@@ -2,7 +2,7 @@ use std::io::Result as Res;
use endio::{LEWrite, Serialize};
use endio::LittleEndian as LE;
use lu_packets_derive::FromVariants;
use lu_packets_derive::{FromVariants, ServiceMessageS};
use crate::common::{ObjId, LuWStr33, ZoneId};
@@ -15,7 +15,7 @@ impl From<ClientMessage> for Message {
}
}
#[derive(Debug, FromVariants)]
#[derive(Debug, FromVariants, ServiceMessageS)]
#[non_exhaustive]
#[repr(u32)]
pub enum ClientMessage {
@@ -24,31 +24,6 @@ pub enum ClientMessage {
CharacterDeleteResponse(CharacterDeleteResponse) = 11,
}
impl<'a, W: LEWrite> Serialize<LE, W> for &'a ClientMessage
where u8: Serialize<LE, W>,
u32: Serialize<LE, W>,
&'a CharacterListResponse: Serialize<LE, W>,
&'a CharacterCreateResponse: Serialize<LE, W>,
&'a CharacterDeleteResponse: Serialize<LE, W> {
fn serialize(self, writer: &mut W) -> Res<()> {
let disc = unsafe { *(self as *const ClientMessage as *const u32) };
writer.write(disc)?;
writer.write(0u8)?;
match self {
ClientMessage::CharacterListResponse(msg) => {
writer.write(msg)?;
}
ClientMessage::CharacterCreateResponse(msg) => {
writer.write(msg)?;
}
ClientMessage::CharacterDeleteResponse(msg) => {
writer.write(msg)?;
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct CharacterListResponse {
pub selected_char: u8,