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
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
/target
|
||||
target
|
||||
.idea
|
||||
.env
|
||||
log
|
||||
log
|
||||
reports
|
||||
47
macros/Cargo.lock
generated
Normal file
47
macros/Cargo.lock
generated
Normal 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
16
macros/Cargo.toml
Normal 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
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()
|
||||
}
|
||||
|
|
@ -284,9 +284,3 @@ mod test{
|
|||
println!("packet: {:?}", packet);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize)]
|
||||
#[rmc_struct(0)]
|
||||
struct MatchmakeParam{
|
||||
params: Vec<(String, Variant)>
|
||||
}
|
||||
|
|
@ -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(())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<u8>)) -> Result<()> {
|
||||
pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut Vec<u8>) -> Result<()> ) -> Result<()> {
|
||||
writer.write_all(&[version])?;
|
||||
|
||||
let mut scratch_space: Vec<u8> = 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<T: Sized>(mut reader: &mut dyn Read, version: u8, pred: impl FnOnce(&mut Cursor<Vec<u8>>) -> Result<T>) -> Result<T> {
|
||||
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)?)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue