init
This commit is contained in:
13
derive_macro/Cargo.toml
Normal file
13
derive_macro/Cargo.toml
Normal 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
139
derive_macro/src/lib.rs
Normal 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())
|
||||
}
|
||||
Reference in New Issue
Block a user