use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Field, Fields};
use crate::utils::{determine_visibility, should_skip};
pub fn impl_setters_macro(ast: &DeriveInput) -> Result<TokenStream, syn::Error> {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let setters = match &ast.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => fields
.named
.iter()
.filter(|f| !should_skip(&f.attrs))
.map(generate_setter)
.collect::<Result<Vec<_>, syn::Error>>()?,
_ => {
return Err(syn::Error::new_spanned(
ast,
"Setters macro only supports structs with named fields",
));
}
},
_ => {
return Err(syn::Error::new_spanned(
ast,
"Setters macro only supports structs",
));
}
};
let expanded = quote! {
impl #impl_generics #name #ty_generics #where_clause {
#(#setters)*
}
};
Ok(TokenStream::from(expanded))
}
fn generate_setter(field: &Field) -> Result<proc_macro2::TokenStream, syn::Error> {
let field_name = field
.ident
.as_ref()
.ok_or_else(|| syn::Error::new_spanned(field, "Field without a name"))?;
let field_type = &field.ty;
let vis = &field.vis;
let setter_name = syn::Ident::new(&format!("set_{}", field_name), field_name.span());
let setter_vis = determine_visibility(vis, &field.attrs)?;
let setter = quote! {
#setter_vis fn #setter_name(&mut self, value: #field_type) {
self.#field_name = value;
}
};
Ok(setter)
}