forked from ObolSquad/trigger-rs
Reviewed-on: ObolSquad/trigger-rs#7 Co-authored-by: traffic95 <traffic95@xeondev.com> Co-committed-by: traffic95 <traffic95@xeondev.com>
76 lines
2.6 KiB
Rust
76 lines
2.6 KiB
Rust
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use syn::{Data, DeriveInput, Fields, Ident, parse_macro_input};
|
|
|
|
pub fn impl_decodeable(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
let input = parse_macro_input!(item as DeriveInput);
|
|
let input_name = &input.ident;
|
|
|
|
match &input.data {
|
|
Data::Struct(st) => {
|
|
let decode_calls = impl_decode_calls_for_fields(&st.fields);
|
|
|
|
quote! {
|
|
impl ::trigger_encoding::Decodeable for #input_name {
|
|
fn decode<R: ::std::io::Read>(r: &mut R) -> ::std::io::Result<Self> {
|
|
Ok(Self {
|
|
#decode_calls
|
|
})
|
|
}
|
|
}
|
|
}
|
|
.into()
|
|
}
|
|
Data::Enum(enumeration) => {
|
|
let repr = input
|
|
.attrs
|
|
.iter()
|
|
.find(|attr| attr.path().is_ident("repr"))
|
|
.expect("#[derive(Decodeable)] requires #[repr(ty)] attribute for enums")
|
|
.parse_args::<Ident>()
|
|
.unwrap();
|
|
|
|
let mut decode_match_arms = TokenStream::new();
|
|
|
|
for variant in enumeration.variants.iter() {
|
|
let variant_name = &variant.ident;
|
|
let (_, discriminant) = variant.discriminant.as_ref().expect(
|
|
"#[derive(Decodeable)] requires explicit enum discriminant declaration",
|
|
);
|
|
|
|
let decode_calls = impl_decode_calls_for_fields(&variant.fields);
|
|
|
|
decode_match_arms.extend(quote! {
|
|
#discriminant => Self::#variant_name {
|
|
#decode_calls
|
|
},
|
|
});
|
|
}
|
|
|
|
quote! {
|
|
impl ::trigger_encoding::Decodeable for #input_name {
|
|
fn decode<R: ::std::io::Read>(r: &mut R) -> ::std::io::Result<Self> {
|
|
Ok(match #repr::decode(r)? {
|
|
#decode_match_arms
|
|
unk => panic!("{}::decode: unknown discriminant encountered: {}", stringify!(#input_name), unk),
|
|
})
|
|
}
|
|
}
|
|
}.into()
|
|
}
|
|
Data::Union(_) => panic!("#[derive(Decodeable)] doesn't support unions"),
|
|
}
|
|
}
|
|
|
|
fn impl_decode_calls_for_fields(fields: &Fields) -> TokenStream {
|
|
let mut decode_calls = TokenStream::new();
|
|
|
|
fields.iter().for_each(|field| {
|
|
let name = field.ident.as_ref().unwrap();
|
|
decode_calls.extend(quote! {
|
|
#name: ::trigger_encoding::Decodeable::decode(r)?,
|
|
});
|
|
});
|
|
|
|
decode_calls
|
|
}
|