#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error2::{abort, abort_call_site, proc_macro_error};
use syn::{parse_macro_input, spanned::Spanned, DataStruct, DeriveInput, Meta};
use crate::generate::{GenMode, GenParams};
mod generate;
#[proc_macro_derive(Getters, attributes(get, with_prefix, getset))]
#[proc_macro_error]
pub fn getters(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let params = GenParams {
mode: GenMode::Get,
global_attr: parse_global_attr(&ast.attrs, GenMode::Get),
};
produce(&ast, ¶ms).into()
}
#[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))]
#[proc_macro_error]
pub fn copy_getters(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let params = GenParams {
mode: GenMode::GetCopy,
global_attr: parse_global_attr(&ast.attrs, GenMode::GetCopy),
};
produce(&ast, ¶ms).into()
}
#[proc_macro_derive(MutGetters, attributes(get_mut, getset))]
#[proc_macro_error]
pub fn mut_getters(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let params = GenParams {
mode: GenMode::GetMut,
global_attr: parse_global_attr(&ast.attrs, GenMode::GetMut),
};
produce(&ast, ¶ms).into()
}
#[proc_macro_derive(Setters, attributes(set, getset))]
#[proc_macro_error]
pub fn setters(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let params = GenParams {
mode: GenMode::Set,
global_attr: parse_global_attr(&ast.attrs, GenMode::Set),
};
produce(&ast, ¶ms).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<syn::Meta> {
use syn::{punctuated::Punctuated, Token};
if attr.path().is_ident("getset") {
let meta_list =
match attr.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
Ok(list) => list,
Err(e) => abort!(attr.span(), "Failed to parse getset attribute: {}", e),
};
let (last, skip, mut collected) = meta_list
.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")
|| meta.path().is_ident("skip"))
{
abort!(meta.path().span(), "unknown setter or getter")
}
})
.fold(
(None, None, Vec::new()),
|(last, skip, mut collected), meta| {
if meta.path().is_ident(mode.name()) {
(Some(meta), skip, collected)
} else if meta.path().is_ident("skip") {
(last, Some(meta), collected)
} else {
collected.push(meta);
(last, skip, collected)
}
},
);
if skip.is_some() {
if last.is_none() && collected.is_empty() {
skip
} else {
abort!(
last.or_else(|| collected.pop()).unwrap().path().span(),
"use of setters and getters with skip is invalid"
);
}
} else {
last
}
} else if attr.path().is_ident(mode.name()) {
attr.meta.clone().into()
} else {
None
}
}
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!");
}
}