extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro2;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error::{abort, abort_call_site, proc_macro_error, ResultExt};
use syn::{spanned::Spanned, DataStruct, DeriveInput, Meta};
mod generate;
use crate::generate::{GenMode, GenParams};
#[proc_macro_derive(Getters, attributes(get, with_prefix, getset))]
#[proc_macro_error]
pub fn getters(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
let params = GenParams {
mode: GenMode::Get,
global_attr: parse_global_attr(&ast.attrs, GenMode::Get),
};
let gen = produce(&ast, ¶ms);
gen.into()
}
#[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))]
#[proc_macro_error]
pub fn copy_getters(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
let params = GenParams {
mode: GenMode::GetCopy,
global_attr: parse_global_attr(&ast.attrs, GenMode::GetCopy),
};
let gen = produce(&ast, ¶ms);
gen.into()
}
#[proc_macro_derive(MutGetters, attributes(get_mut, getset))]
#[proc_macro_error]
pub fn mut_getters(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
let params = GenParams {
mode: GenMode::GetMut,
global_attr: parse_global_attr(&ast.attrs, GenMode::GetMut),
};
let gen = produce(&ast, ¶ms);
gen.into()
}
#[proc_macro_derive(Setters, attributes(set, getset))]
#[proc_macro_error]
pub fn setters(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for setters");
let params = GenParams {
mode: GenMode::Set,
global_attr: parse_global_attr(&ast.attrs, GenMode::Set),
};
let gen = produce(&ast, ¶ms);
gen.into()
}
fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
attrs
.iter()
.filter_map(|v| parse_attr(v, mode)) .last()
}
fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<Meta> {
use syn::{punctuated::Punctuated, Token};
if attr.path.is_ident("getset") {
attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
.unwrap_or_abort()
.into_iter()
.inspect(|meta| {
if !(meta.path().is_ident("get")
|| meta.path().is_ident("get_copy")
|| meta.path().is_ident("get_mut")
|| meta.path().is_ident("set"))
{
abort!(meta.path().span(), "unknown setter or getter")
}
})
.filter(|meta| meta.path().is_ident(mode.name()))
.last()
} else {
attr.parse_meta()
.ok()
.filter(|meta| meta.path().is_ident(mode.name()))
}
}
fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
let name = &ast.ident;
let generics = &ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
if let syn::Data::Struct(DataStruct { ref fields, .. }) = ast.data {
let generated = fields.iter().map(|f| generate::implement(f, params));
quote! {
impl #impl_generics #name #ty_generics #where_clause {
#(#generated)*
}
}
} else {
abort_call_site!("#[derive(Getters)] is only defined for structs, not for enums!");
}
}