use proc_macro::TokenStream;
use quote::ToTokens;
use syn::parse::Parse;
use syn::spanned::Spanned;
use syn::{
parse_macro_input, parse_quote, Attribute, Error, Fields, ItemEnum, ItemStruct, LitInt, Type,
};
mod derive_impl;
fn get_list_attr_or_default<T>(key: &str, default: T, attributes: &[Attribute]) -> T
where
T: Parse,
{
for attribute in attributes {
if attribute.meta.path().is_ident(key) {
return attribute
.meta
.require_list()
.unwrap()
.parse_args::<T>()
.unwrap();
}
}
default
}
fn derive_empty_item_error<T>(tokens: T) -> TokenStream
where
T: ToTokens,
{
Error::new_spanned(tokens, "This macro cannot be derived on empty items")
.into_compile_error()
.into()
}
#[proc_macro_derive(ReadEnum, attributes(type_variant_id))]
pub fn derive_read_enum(tokens: TokenStream) -> TokenStream {
let item = parse_macro_input!(tokens as ItemEnum);
if item.variants.is_empty() {
return derive_empty_item_error(item);
}
let path = get_list_attr_or_default("type_variant_id", parse_quote!(u8), &item.attrs);
derive_impl::impl_read_enum(item.ident, path, item.variants.into_iter()).into()
}
#[proc_macro_derive(ReadStruct)]
pub fn derive_read_struct(tokens: TokenStream) -> TokenStream {
let item = parse_macro_input!(tokens as ItemStruct);
if item.fields.is_empty() {
return derive_empty_item_error(item);
}
match item.fields {
Fields::Named(f) => derive_impl::impl_read_struct_named(
item.ident,
f.named.into_iter().map(|f| f.ident.unwrap()),
),
Fields::Unnamed(f) => {
derive_impl::impl_read_struct_tuple(item.ident, f.unnamed.into_iter().map(|f| f.ty))
}
Fields::Unit => return derive_empty_item_error(item),
}
.into()
}
#[proc_macro_derive(WriteStruct)]
pub fn derive_write_struct(tokens: TokenStream) -> TokenStream {
let item = parse_macro_input!(tokens as ItemStruct);
if item.fields.is_empty() {
return derive_empty_item_error(item);
}
let types = item.fields.into_iter().enumerate().map(|(i, f)| {
f.ident
.as_ref()
.map(|i| i.to_token_stream())
.unwrap_or_else(|| LitInt::new(&i.to_string(), f.span()).to_token_stream())
});
derive_impl::impl_write_struct(item.ident, types).into()
}
#[proc_macro_derive(WriteEnum, attributes(type_variant_id))]
pub fn derive_write_enum(tokens: TokenStream) -> TokenStream {
let item = parse_macro_input!(tokens as ItemEnum);
if item.variants.is_empty() {
return derive_empty_item_error(item);
}
let ty: Type = get_list_attr_or_default("type_variant_id", parse_quote!(u8), &item.attrs);
derive_impl::impl_write_enum(item.ident, ty, item.variants.into_iter()).into()
}