use darling::{Error, FromDeriveInput, FromField, FromMeta};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error2::{abort_call_site, proc_macro_error};
use quote::quote;
use syn::{parse_macro_input, parse_str, DeriveInput, GenericArgument, Meta, PathArguments, Type, TypePath};
mod generate;
#[proc_macro_derive(Getter, attributes(get))]
#[proc_macro_error]
pub fn getter(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let result = Getter::from_derive_input(&ast);
let mut getter = match result {
Ok(input) => input,
Err(e) => return Error::custom(e).write_errors().into(),
};
expand(&mut getter).into()
}
fn expand(getter: &mut Getter) -> TokenStream2 {
let name = &getter.ident;
let generics = &getter.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
if let darling::ast::Data::Struct(ref mut fields) = getter.data {
let generated = fields.fields
.iter_mut()
.map(|f| {
let field_ty = &f.ty;
let inner_type = extract_option_inner_type(&field_ty);
match inner_type {
Some(ref ty) => {
let is_option = true;
let primitive = primitives().contains(ty);
let string = string().eq(ty);
f.is_option = is_option;
f.is_primitive = primitive;
f.is_string = string;
},
None => {
let is_option = false;
f.is_option = is_option;
}
}
f.inner_type = inner_type;
f
})
.filter(|f| !f.is_option || (f.is_string || f.is_primitive))
.filter(|f| !f.skip)
.map(|f| generate::implement(f));
quote! {
impl #impl_generics #name #ty_generics #where_clause {
#(#generated)*
}
}
} else {
abort_call_site!("#[derive(Getters)]只能定义在结构体上");
}
}
fn extract_option_inner_type(ty: &Type) -> Option<Type> {
if let Type::Path(type_path) = ty {
let segment = type_path.path.segments.last()?;
if segment.ident == "Option" {
if let PathArguments::AngleBracketed(args) = &segment.arguments {
if let Some(GenericArgument::Type(inner_ty)) = args.args.first() {
return Some(inner_ty.clone());
}
}
}
}
None
}
fn primitives() -> [Type; 16] {
[
Type::Path(create_type_path("i8")),
Type::Path(create_type_path("i16")),
Type::Path(create_type_path("i32")),
Type::Path(create_type_path("i64")),
Type::Path(create_type_path("i128")),
Type::Path(create_type_path("u8")),
Type::Path(create_type_path("u16")),
Type::Path(create_type_path("u32")),
Type::Path(create_type_path("u64")),
Type::Path(create_type_path("u128")),
Type::Path(create_type_path("isize")),
Type::Path(create_type_path("usize")),
Type::Path(create_type_path("bool")),
Type::Path(create_type_path("char")),
Type::Path(create_type_path("f32")),
Type::Path(create_type_path("f64")),
]
}
fn string() -> Type {
Type::Path(create_type_path("String"))
}
fn create_type_path(path: &str) -> TypePath {
let result = parse_str::<TypePath>(path);
match result {
Ok(ty) => ty,
Err(_) => abort_call_site!("类型解析失败"),
}
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(get), supports(struct_named))]
struct Getter {
pub ident: syn::Ident,
pub generics: syn::Generics,
pub data: darling::ast::Data<(), GetterField>,
}
#[derive(Debug, Clone, FromField)]
#[darling(attributes(get))]
struct GetterField {
pub ident: Option<syn::Ident>,
pub ty: Type,
#[darling(default)]
pub skip: bool,
#[darling(default, rename = "pub")]
pub public: bool,
pub default: Option<DefaultValue>,
#[darling(skip, default)]
pub is_option: bool,
#[darling(skip, default)]
pub inner_type: Option<Type>,
#[darling(skip, default)]
pub is_string: bool,
#[darling(skip, default)]
pub is_primitive: bool,
}
#[derive(Debug, Clone)]
enum DefaultValue {
NoValue, Value(syn::Expr), }
impl Default for DefaultValue {
fn default() -> Self {
DefaultValue::NoValue
}
}
impl FromMeta for DefaultValue {
fn from_meta(meta: &Meta) -> darling::Result<Self> {
match meta {
Meta::Path(_) => {
Ok(DefaultValue::NoValue)
}
Meta::NameValue(mnv) => {
Ok(DefaultValue::Value(mnv.value.clone()))
}
Meta::List(_) => Err(Error::unsupported_format("list")),
}
}
}