use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Field, Generics, Ident};
pub fn impl_get(
DeriveInput {
data,
generics,
ident: name,
..
}: &DeriveInput,
) -> proc_macro2::TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
match data {
Data::Struct(DataStruct { fields, .. }) => {
let methods = fields
.iter()
.map(|field| _handle_field(field, generics, name));
return quote! {
impl #impl_generics #name #ty_generics #where_clause {
#(#methods)*
}
};
}
_ => panic!("The `Get` macro can only be derived for structs"),
}
}
fn _handle_field(
field: &Field,
generics: &Generics,
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let methods = match field.ident {
Some(_) => handle_get_named(field, generics, name),
None => handle_get_unnamed(field, generics, name),
};
quote! {
#methods
}
}
fn handle_get_named(
field: &Field,
generics: &Generics,
_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let Field {
ident,
ty: field_type,
..
} = field;
let _where_clause_u = generics.where_clause.as_ref();
let field_name = ident.as_ref().unwrap();
let get = field_name;
let get_mut = quote::format_ident!("{}_mut", field_name);
let into = quote::format_ident!("into_{}", field_name);
quote! {
pub const fn #get(&self) -> &#field_type {
&self.#field_name
}
pub const fn #get_mut(&mut self) -> &mut #field_type {
&mut self.#field_name
}
#[inline]
pub fn #into(self) -> #field_type {
self.#field_name
}
}
}
fn handle_get_unnamed(
field: &Field,
_generics: &Generics,
_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let field_type = &field.ty;
quote! {
pub const fn get(&self) -> &#field_type {
&self.0
}
pub const fn get_mut(&mut self) -> &mut #field_type {
&mut self.0
}
#[inline]
pub fn value(self) -> #field_type {
self.0
}
}
}
fn _convert_generic_where_clause(
new_ident: &Ident,
clause: &syn::WhereClause,
) -> proc_macro2::TokenStream {
let predicates = clause.predicates.iter().map(|p| {
if let syn::WherePredicate::Type(inner) = p {
let mut pred = inner.clone();
pred.bounded_ty = if let syn::Type::Verbatim(_ty) = &inner.bounded_ty {
syn::Type::Verbatim(quote!(#new_ident))
} else {
inner.bounded_ty.clone()
};
return quote!(#pred);
}
quote!(#p)
});
quote! {
where #(#predicates),*
}
}