feat(secure): add RmcSerialize macro for convenience serializing structs
This commit is contained in:
parent
d01acbb931
commit
7479105157
7 changed files with 275 additions and 11 deletions
185
macros/src/lib.rs
Normal file
185
macros/src/lib.rs
Normal 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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue