diff --git a/psopacket/src/lib.rs b/psopacket/src/lib.rs index f16ab22..7d0358e 100644 --- a/psopacket/src/lib.rs +++ b/psopacket/src/lib.rs @@ -6,7 +6,8 @@ extern crate proc_macro; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemStruct, NestedMeta}; +use syn::{parse_macro_input, ItemStruct, NestedMeta, DeriveInput, Field}; +use syn::punctuated::Iter; use quote::quote; #[derive(Debug, PartialEq)] @@ -45,7 +46,7 @@ fn generate_struct_def(name: syn::Ident, attrs: &Vec) -> proc_macro2:: } -fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec, include_flag: bool) -> proc_macro2::TokenStream { +fn generate_from_bytes(attrs: &Vec) -> Vec { let mut from_bytes = Vec::new(); for attr in attrs { let element = match attr { @@ -100,7 +101,10 @@ fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec }; from_bytes.push(element); } + from_bytes +} +fn generate_as_bytes(attrs: &Vec) -> Vec { let mut as_bytes = Vec::new(); for attr in attrs { let element = match attr { @@ -130,6 +134,13 @@ fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec }; as_bytes.push(element); } + as_bytes +} + + +fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec, include_flag: bool) -> proc_macro2::TokenStream { + let from_bytes = generate_from_bytes(&attrs); + let as_bytes = generate_as_bytes(&attrs); quote! { impl PSOPacket for #name { @@ -280,38 +291,14 @@ fn generate_partialeq_impl(name: syn::Ident, attrs: &Vec) -> proc_macr } -#[proc_macro_attribute] -pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { - let args = parse_macro_input!(attr as syn::AttributeArgs); - let mut cmd = 0; - let mut flag = true; - for a in args { - match &a { - NestedMeta::Lit(lit) => { - if let syn::Lit::Int(litint) = lit { - cmd = litint.base10_parse().unwrap(); - } - }, - NestedMeta::Meta(k) => { - if let syn::Meta::Path(syn::Path {segments, ..}) = k { - match segments[0].ident.to_string().as_str() { - "no_flag" => flag = false, - _ => { - return syn::Error::new(segments[0].ident.span(), "unknown macro param").to_compile_error().into(); - } - } - } - }, - } - } +fn get_struct_fields(fields: Iter) -> Result, TokenStream> { let mut attrs = Vec::new(); - let pkt_struct = parse_macro_input!(item as ItemStruct); let mut must_be_last = false; - for field in pkt_struct.fields.iter() { + for field in fields { if must_be_last { - return syn::Error::new(field.ident.as_ref().unwrap().span(), "variables can not follow Vec or String").to_compile_error().into(); + return Err(syn::Error::new(field.ident.as_ref().unwrap().span(), "variables can not follow Vec or String").to_compile_error().into()); } let mut attr_meta = AttrMeta::None; for attr in &field.attrs { @@ -346,6 +333,40 @@ pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { _ => {} } } + Ok(attrs) +} + + +#[proc_macro_attribute] +pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attr as syn::AttributeArgs); + let mut cmd = 0; + let mut flag = true; + for a in args { + match &a { + NestedMeta::Lit(lit) => { + if let syn::Lit::Int(litint) = lit { + cmd = litint.base10_parse().unwrap(); + } + }, + NestedMeta::Meta(k) => { + if let syn::Meta::Path(syn::Path {segments, ..}) = k { + match segments[0].ident.to_string().as_str() { + "no_flag" => flag = false, + _ => { + return syn::Error::new(segments[0].ident.span(), "unknown macro param").to_compile_error().into(); + } + } + } + }, + } + } + + let pkt_struct = parse_macro_input!(item as ItemStruct); + let attrs = match get_struct_fields(pkt_struct.fields.iter()) { + Ok(a) => a, + Err(err) => return err + }; let struct_def = generate_struct_def(pkt_struct.ident.clone(), &attrs); let psopacket_impl = generate_psopacket_impl(cmd, pkt_struct.ident.clone(), &attrs, flag); @@ -362,3 +383,44 @@ pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { q.into() } + +#[proc_macro_derive(PSOPacketData)] +pub fn pso_packet_data(input: TokenStream) -> TokenStream { + let derive = parse_macro_input!(input as DeriveInput); + + let name = derive.ident; + + let fields = if let syn::Data::Struct(strct) = derive.data { + strct.fields + } + else { + return syn::Error::new(name.span(), "PSOPacketData only works on structs").to_compile_error().into(); + }; + + let attrs = match get_struct_fields(fields.iter()) { + Ok(a) => a, + Err(err) => return err + }; + + let from_bytes = generate_from_bytes(&attrs); + let as_bytes = generate_as_bytes(&attrs); + + let impl_pso_data_packet = quote! { + impl PSOPacketData for #name { + fn from_bytes(mut cur: &mut R) -> Result { + Ok(#name { + #(#from_bytes)* + }) + } + + fn as_bytes(&self) -> Vec { + let mut buf = Vec::new(); + #(#as_bytes)* + buf + } + } + }; + + + impl_pso_data_packet.into() +}