#![recursion_limit = "128"]
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
#[derive(Clone, Copy)]
enum Endianness {
Little,
Big,
Unspecified,
}
#[proc_macro_derive(ByteStruct, attributes(byte_struct_le, byte_struct_be))]
pub fn byte_struct_macro_derive(input: TokenStream) -> TokenStream {
byte_struct_macro_derive_impl(input, Endianness::Unspecified)
}
#[deprecated]
#[proc_macro_derive(ByteStructLE)]
pub fn byte_struct_le_macro_derive(input: TokenStream) -> TokenStream {
byte_struct_macro_derive_impl(input, Endianness::Little)
}
#[deprecated]
#[proc_macro_derive(ByteStructBE)]
pub fn byte_struct_be_macro_derive(input: TokenStream) -> TokenStream {
byte_struct_macro_derive_impl(input, Endianness::Big)
}
fn byte_struct_macro_derive_impl(input: TokenStream, endianness_input: Endianness) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let mut found_le = false;
let mut found_be = false;
for attr in ast.attrs {
let syn::Attribute{meta: syn::Meta::Path(syn::Path{segments, ..}), ..} = attr else {continue};
if segments.len() != 1 {
continue;
}
match segments[0].ident.to_string().as_str() {
"byte_struct_le" => found_le = true,
"byte_struct_be" => found_be = true,
_ => ()
};
}
if found_be && found_le {
panic!("Found conflicting byte_struct_le and byte_struct_be attributes");
}
let endianness = if found_le {
Endianness::Little
} else if found_be {
Endianness::Big
} else {
endianness_input
};
let name = &ast.ident;
if let syn::Data::Struct(syn::DataStruct{fields: syn::Fields::Named(
syn::FieldsNamed{named, ..}), ..}) = ast.data {
let mut ty0 = Vec::<syn::Type>::new();
let mut ident1 = Vec::<syn::Ident>::new();
let mut field_endianness = Vec::<Endianness>::new();
for n in named {
ty0.push(n.ty.clone());
ident1.push(n.ident.unwrap().clone());
let mut found_le = false;
let mut found_be = false;
for attr in n.attrs {
let syn::Attribute{meta: syn::Meta::Path(syn::Path{segments, ..}), ..} = attr else {continue};
if segments.len() != 1 {
continue;
}
match segments[0].ident.to_string().as_str() {
"byte_struct_le" => found_le = true,
"byte_struct_be" => found_be = true,
_ => ()
};
}
if found_be && found_le {
panic!("Found conflicting byte_struct_le and byte_struct_be attributes");
}
if found_be {
field_endianness.push(Endianness::Big);
} else if found_le {
field_endianness.push(Endianness::Little);
} else {
field_endianness.push(endianness);
}
}
let (write_bytes_fn, read_bytes_fn): (Vec<_>, Vec<_>) =
field_endianness.iter().map(|e| {
let name_str = match e {
Endianness::Little => ("write_bytes_default_le", "read_bytes_default_le"),
Endianness::Big => ("write_bytes_default_be", "read_bytes_default_be"),
Endianness::Unspecified => ("write_bytes", "read_bytes"),
};
(syn::Ident::new(name_str.0, Span::call_site()),
syn::Ident::new(name_str.1, Span::call_site()))
}).unzip();
let ty1 = ty0.clone();
let ty2 = ty0.clone();
let ty3 = ty0.clone();
let ident2 = ident1.clone();
let ident3 = ident1.clone();
let gen = quote! {
impl ByteStruct for #name {
fn write_bytes(&self, bytes: &mut [u8]) {
let mut cur: usize = 0;
#({
let len = <#ty1>::BYTE_LEN;
self.#ident1.#write_bytes_fn(&mut bytes[cur .. (cur + len)]);
cur += len;
})*
}
fn read_bytes(bytes: &[u8]) -> Self {
let mut cur: usize = 0;
#(
let len = <#ty2>::BYTE_LEN;
let #ident2 = <#ty3>::#read_bytes_fn(&bytes[cur .. (cur + len)]);
cur += len;
)*
#name { #(#ident3),* }
}
}
impl ByteStructLen for #name {
const BYTE_LEN: usize = #(<#ty0>::BYTE_LEN)+*;
}
};
gen.into()
} else {
panic!("Only support struct with named fields!");
}
}