This commit is contained in:
Numbers
2025-05-29 03:45:41 +02:00
commit ebf6bde22d
10 changed files with 654 additions and 0 deletions

13
derive_macro/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "derive_macro"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
syn = { version = "2.0.101", features = ["full", "extra-traits"] }
proc-macro2 = "1.0"
quote = "1"

139
derive_macro/src/lib.rs Normal file
View File

@@ -0,0 +1,139 @@
#![feature(try_blocks)]
use proc_macro::TokenStream;
use std::fmt::Write;
use proc_macro2::Ident;
use quote::ToTokens;
use syn::{Data, DataStruct, DeriveInput, Fields, parse_macro_input};
#[proc_macro_derive(OverTheWire)]
pub fn derive_bin_serializer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match input.data {
Data::Struct(data) => { derive_struct(input.ident, &data).unwrap() }
Data::Enum(_data) => { panic!("Cannot derive OverTheWire for Enums (yet)"); }
Data::Union(_data) => { panic!("Cannot derive OverTheWire for Unions"); }
}
}
#[allow(unused_variables)]
fn derive_struct(name: Ident, data: &DataStruct) -> Result<TokenStream, std::fmt::Error> {
let mut output = String::new();
// collect the fields...
let input_fields = match &data.fields {
// field_name: <value>
Fields::Named(fields) =>
fields.named.iter().filter_map(
|n| { n.ident.as_ref().map(|i| i.to_string()) }
).collect::<Vec<String>>(),
// field names are 0, 1, 2, etc.
Fields::Unnamed(fields) =>
(0..fields.unnamed.len())
.map(|i| i.to_string())
.collect::<Vec<String>>(),
// no fields to worry about
Fields::Unit => Vec::new(),
};
const PAD: &str = " ";
let deserialize = match &data.fields {
Fields::Named(_) => {
let mut output = String::new();
writeln!(&mut output, "{PAD}Ok(Self{{")?;
for fname in &input_fields {
writeln!(&mut output, "{PAD} {fname}: otw::OverTheWire::deserialize(reader)?,")?
}
writeln!(&mut output, "{PAD}}})")?;
output
}
Fields::Unnamed(_) => {
let mut output = String::new();
writeln!(&mut output, "{PAD}Ok(Self(")?;
for fname in &input_fields {
writeln!(&mut output, "{PAD} otw::OverTheWire::deserialize(reader)?,")?
}
writeln!(&mut output, "{PAD}))")?;
output
}
Fields::Unit => "Ok(Self)".to_string(),
};
let serialize = match data.fields {
Fields::Named(_) | Fields::Unnamed(_) => {
let mut output = String::new();
for fname in &input_fields {
writeln!(&mut output, "{PAD}self.{fname}.serialize(writer)?;")?
}
writeln!(&mut output, "{PAD}Ok(())")?;
output
}
Fields::Unit => "Ok(())".to_string(),
};
let input_types = match &data.fields {
// field_name: <value>
Fields::Named(fields) =>
fields.named.iter()
.map(|n| { n.ty.to_token_stream().to_string() })
.collect::<Vec<String>>(),
// field names are 0, 1, 2, etc.
Fields::Unnamed(fields) => fields.unnamed.iter()
.map(|f|f.ty.to_token_stream().to_string())
.collect::<Vec<String>>(),
// no fields to worry about
Fields::Unit => Vec::new(),
};
let size_hint = match data.fields {
Fields::Named(_) | Fields::Unnamed(_) => {
let mut output = String::new();
writeln!(&mut output, "{PAD}0usize")?;
for ftype in &input_types {
writeln!(&mut output, "{PAD} .saturating_add(otw::min_wire_size::<{ftype}>())")?
}
output
}
Fields::Unit => "Ok(())".to_string(),
};
writeln!(&mut output, "#[automatically_derived]")?;
writeln!(&mut output, "impl otw::OverTheWire for {name} {{")?;
writeln!(&mut output, " fn serialize<T: otw::Writer>(&self, writer: &mut T) -> e::Result<()> {{")?;
writeln!(&mut output, "{serialize}")?;
writeln!(&mut output, " }}")?;
writeln!(&mut output, " fn deserialize<T: otw::Reader>(reader: &mut T) -> e::Result<Self> {{")?;
writeln!(&mut output, "{deserialize}")?;
writeln!(&mut output, " }}")?;
writeln!(&mut output, " fn size_hint() -> usize {{")?;
writeln!(&mut output, "{size_hint}")?;
writeln!(&mut output, " }}")?;
/*
fn size_hint() -> usize {
size_of::<Self>()
}
*/
writeln!(&mut output, "}}")?;
if let Err(error) = output.parse::<TokenStream>() {
panic!("{}", output);
}
Ok(output.parse().unwrap())
}