diff --git a/.gitignore b/.gitignore index e46d73b..ea0ae4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -/target +target .idea .env -log \ No newline at end of file +log +reports \ No newline at end of file diff --git a/macros/Cargo.lock b/macros/Cargo.lock new file mode 100644 index 0000000..b79f6d0 --- /dev/null +++ b/macros/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..a8c2072 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "macros" +version = "0.0.0" +authors = ["DJMrTV "] +description = "A `cargo generate` template for quick-starting a procedural macro crate" +keywords = ["template", "proc_macro", "procmacro"] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +proc-macro2 = "1.0" +syn = "1.0" + diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..1652f28 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,185 @@ +extern crate proc_macro; + +use proc_macro2::TokenTree; +use quote::__private::ext::RepToTokensExt; +use proc_macro::TokenStream; + +use syn::{parse_macro_input, DeriveInput, Data}; +use quote::{quote, TokenStreamExt}; + + +/// Example of user-defined [derive mode macro][1] +/// +/// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-mode-macros +#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))] +pub fn rmc_serialize(input: TokenStream) -> TokenStream { + let derive_input = parse_macro_input!(input as DeriveInput); + + let struct_attr = derive_input.attrs.iter() + .find(|a| a.path.segments.len() == 1 && + a.path.segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); + + let Data::Struct(s) = derive_input.data else { + panic!("rmc struct type MUST be a struct"); + }; + + /// generate base data + + let serialize_base_content = { + let mut serialize_content = quote! {}; + + for f in &s.fields{ + if f.attrs.iter() + .any(|a| a.path.segments.len() == 1 && + a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + continue; + } + let ident = f.ident.as_ref().unwrap(); + + serialize_content.append_all(quote!{ + self.#ident.serialize(writer)?; + }) + } + + quote!{ + #serialize_content + + Ok(()) + } + }; + + let struct_ctor = { + let mut structure_content = quote! {}; + for f in &s.fields { + let ident = f.ident.as_ref().unwrap(); + + structure_content.append_all(quote!{#ident, }); + } + + quote!{ + Ok(Self{ + #structure_content + }) + } + }; + + let deserialize_base_content = { + let mut deserialize_content = quote! {}; + + for f in &s.fields{ + if f.attrs.iter() + .any(|a| a.path.segments.len() == 1 && + a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + continue; + } + + let ident = f.ident.as_ref().unwrap(); + let ty = &f.ty; + + deserialize_content.append_all(quote!{ + let #ident = <#ty> :: deserialize(reader)?; + }) + } + + quote!{ + #deserialize_content + #struct_ctor + } + }; + + /// generate base with extends stuff + + let serialize_base_content = if let Some(attr) = struct_attr{ + let tokens = attr.tokens.clone(); + let token = tokens.into_iter().next().unwrap(); + + let version = match token { + TokenTree::Group(g) => { + match g.stream().into_iter().next().unwrap(){ + TokenTree::Literal(l) => l, + _ => panic!("expected literal") + } + }, + _ => panic!("expected group") + }; + + let pre_inner = if let Some(f) = s.fields.iter().find(|f| { + f.attrs.iter() + .any(|a| a.path.segments.len() == 1 && + a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + }){ + let ident= f.ident.as_ref().unwrap(); + quote! { + self.#ident.serialize(writer)?; + } + } else { + quote! {} + }; + + quote! { + #pre_inner + crate::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{ + #serialize_base_content + })?; + + Ok(()) + } + } else { + serialize_base_content + }; + + let deserialize_base_content = if let Some(attr) = struct_attr{ + let tokens = attr.tokens.clone(); + let token = tokens.into_iter().next().unwrap(); + + let version = match token { + TokenTree::Group(g) => { + match g.stream().into_iter().next().unwrap(){ + TokenTree::Literal(l) => l, + _ => panic!("expected literal") + } + }, + _ => panic!("expected group") + }; + let pre_inner = if let Some(f) = s.fields.iter().find(|f| { + f.attrs.iter() + .any(|a| a.path.segments.len() == 1 && + a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + }){ + let ident= f.ident.as_ref().unwrap(); + let ty= &f.ty; + quote! { + let #ident = <#ty> :: deserialize(reader)?; + } + } else { + quote! {} + }; + + quote! { + #pre_inner + Ok(crate::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ + #deserialize_base_content + })?) + } + } else { + deserialize_base_content + }; + + let ident = derive_input.ident; + + let tokens = quote! { + impl crate::rmc::structures::RmcSerialize for #ident{ + fn serialize(&self, writer: &mut dyn ::std::io::Write) -> crate::rmc::structures::Result<()>{ + #serialize_base_content + + + } + + fn deserialize(reader: &mut dyn ::std::io::Read) -> crate::rmc::structures::Result{ + #deserialize_base_content + } + } + }; + + tokens.into() +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d893311..96d4ab9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -284,9 +284,3 @@ mod test{ println!("packet: {:?}", packet); } } - -#[derive(RmcSerialize)] -#[rmc_struct(0)] -struct MatchmakeParam{ - params: Vec<(String, Variant)> -} \ No newline at end of file diff --git a/src/rmc/structures/connection_data.rs b/src/rmc/structures/connection_data.rs index a0fc79a..2527c1b 100644 --- a/src/rmc/structures/connection_data.rs +++ b/src/rmc/structures/connection_data.rs @@ -17,6 +17,8 @@ impl<'a> RmcSerialize for ConnectionData<'a>{ self.special_protocols.serialize(v).expect("unable to write special protocols"); self.special_station_url.serialize(v).expect("unable to write special station url"); v.write_all(bytes_of(&self.date_time)).expect("unable to write date time"); + + Ok(()) }) } diff --git a/src/rmc/structures/rmc_struct.rs b/src/rmc/structures/rmc_struct.rs index 165a10e..fc4c944 100644 --- a/src/rmc/structures/rmc_struct.rs +++ b/src/rmc/structures/rmc_struct.rs @@ -1,5 +1,7 @@ -use std::io::Write; +use std::io::{Cursor, Read, Write}; use bytemuck::bytes_of; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use crate::rmc::structures::Error::VersionMismatch; use crate::rmc::structures::Result; #[repr(C, packed)] @@ -8,12 +10,12 @@ struct StructureHeader{ length: u32 } -pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl Fn(&mut Vec)) -> Result<()> { +pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut Vec) -> Result<()> ) -> Result<()> { writer.write_all(&[version])?; let mut scratch_space: Vec = Vec::new(); - (pred)(&mut scratch_space); + (pred)(&mut scratch_space)?; let u32_size= scratch_space.len() as u32; @@ -23,3 +25,20 @@ pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl Fn(&mut Ok(()) } +pub fn read_struct(mut reader: &mut dyn Read, version: u8, pred: impl FnOnce(&mut Cursor>) -> Result) -> Result { + let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?; + + if ver != version{ + return Err(VersionMismatch(ver)); + } + + let size: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + + let mut vec = vec![0u8; size as usize]; + + reader.read_exact(&mut vec)?; + + let mut cursor = Cursor::new(vec); + + Ok(pred(&mut cursor)?) +} \ No newline at end of file