use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Field, Generics, Ident};
pub fn impl_wrapper(input: &DeriveInput) -> proc_macro2::TokenStream {
let DeriveInput {
data,
generics,
ident: name,
.. } = input;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
if let Data::Struct(DataStruct { fields, .. }) = data {
if fields.len() != 1 {
panic!("The `Wrapper` macro can only be derived for single field structs");
}
let methods = fields
.iter()
.map(|field| _handle_field(field, generics, name));
return quote! {
impl #impl_generics #name #ty_generics #where_clause {
#(#methods)*
}
};
}
panic!("The `Wrapper` macro can only be derived for single field structs");
}
fn _handle_field(
field: &Field,
generics: &Generics,
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let Field {
ident: field_name,
ty: field_type,
..
} = field;
let methods = match field_name {
Some(_) => _handle_named(field, generics, name),
None => _handle_unnamed(field, generics, name),
};
quote! {
#methods
#[inline]
pub fn map<U, F>(self, f: F) -> #name<U>
where
F: FnOnce(#field_type) -> U,
{
#name::new(f(self.value()))
}
pub const fn replace(&mut self, value: #field_type) -> #field_type {
::core::mem::replace(self.get_mut(), value)
}
#[inline]
pub fn set(&mut self, value: #field_type) -> &mut Self {
*self.get_mut() = value;
self
}
pub const fn swap(&mut self, other: &mut Self) {
::core::mem::swap(self.get_mut(), other.get_mut());
}
#[inline]
pub fn take(&mut self) -> #field_type
where
#field_type: Default
{
::core::mem::take(self.get_mut())
}
pub const fn view(&self) -> #name<&#field_type> {
#name::new(self.get())
}
pub const fn view_mut(&mut self) -> #name<&mut #field_type> {
#name::new(self.get_mut())
}
}
}
fn _handle_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();
if ident.is_none() {
panic!("The `Wrapper` macro can only be derived for single field structs");
}
let field_name = ident.as_ref().unwrap();
quote! {
pub const fn new(#field_name: #field_type) -> Self {
Self { #field_name }
}
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 value(self) -> #field_type {
self.#field_name
}
}
}
fn _handle_unnamed(
field: &Field,
_generics: &Generics,
_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let field_type = &field.ty;
quote! {
pub const fn new(value: #field_type) -> Self {
Self(value)
}
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),*
}
}