moxy-derive 0.0.4

derive macros for moxy crate
Documentation
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)*
            }
        })
    }
}