use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::{Render, core::Field, params};
use super::syntax::SetSyntax;
#[derive(Clone, Default)]
pub struct StructSyntax;
impl Render for StructSyntax {
type Args = params::StructParams;
fn render(&self, args: Self::Args) -> syn::Result<TokenStream> {
let ident = &args.input.ident;
let (impl_generics, type_generics, where_generics) = &args.input.generics.split_for_impl();
let fields: Vec<_> = args
.data
.fields
.iter()
.enumerate()
.map(|(i, field)| Field::parse(i, field))
.collect::<syn::Result<Vec<_>>>()?;
let fields: Vec<_> = fields
.into_iter()
.filter(|field| field.attrs().exists("set"))
.collect();
let methods: Vec<TokenStream> = fields
.iter()
.map(|field| -> syn::Result<TokenStream> {
let fname = field.name();
let doc_attrs = field.docs();
let is_option = field.is_option();
let opts = SetSyntax::parse(field)?;
let method_name = match opts.custom_name {
Some(id) => id,
None => format_ident!("set_{}", fname.to_string()),
};
let setter_ty = if is_option {
let inner = field.as_inner().unwrap();
quote!(#inner)
} else {
let ty = field.ty();
quote!(#ty)
};
let body = if let Some(on_expr) = opts.on {
if is_option {
quote! {
let value: #setter_ty = value.into();
self.#fname = ::std::option::Option::Some(#on_expr);
}
} else {
quote! {
let value: #setter_ty = value.into();
self.#fname = #on_expr;
}
}
} else if is_option {
quote! {
self.#fname = ::std::option::Option::Some(value.into());
}
} else {
quote! {
self.#fname = value.into();
}
};
Ok(quote! {
#(#doc_attrs)*
pub fn #method_name<V: ::std::convert::Into<#setter_ty>>(&mut self, value: V) -> &mut Self {
#body
self
}
})
})
.collect::<syn::Result<Vec<_>>>()?;
Ok(quote! {
impl #impl_generics #ident #type_generics #where_generics {
#(#methods)*
}
})
}
}