feat(secure): add RmcSerialize macro for convenience serializing structs

This commit is contained in:
DJMrTV 2025-02-05 15:03:08 +01:00
commit 7479105157
7 changed files with 275 additions and 11 deletions

47
macros/Cargo.lock generated Normal file
View file

@ -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"

16
macros/Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "macros"
version = "0.0.0"
authors = ["DJMrTV <tvnebel@gmail.com>"]
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"

185
macros/src/lib.rs Normal file
View file

@ -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<Self>{
#deserialize_base_content
}
}
};
tokens.into()
}