builders 0.5.0

Rust macros for building structs
Documentation
mod util;
#[cfg(feature = "builder")]
mod builder;

use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use syn::{Meta, MetaList};

#[cfg(feature = "builder")]
#[proc_macro_derive(Builder, attributes(builder))]
pub fn builder_derive(input: TokenStream) -> TokenStream {
    builder::builder_derive_impl(input)
}


#[cfg(feature = "getters")]
#[proc_macro_derive(Getters, attributes(getter,getters))]
pub fn getters_derive(input: TokenStream) -> TokenStream {
    use quote::quote;
    use syn::{parse_macro_input, DeriveInput};
    let ast = parse_macro_input!(input as DeriveInput);

    let prefix = util::get_prefix(&ast.attrs, "get_", "getters");

    util::gen_for_each_field(&ast, "getter", &prefix, |field,_each| {
        let ident = util::get_field_ident(field);
        let ty = &field.ty;
        quote! {
            (&self) -> & #ty {
                & self.#ident
            }
        }
    })
}

#[cfg(feature = "setters")]
#[proc_macro_derive(Setters, attributes(setter,setters))]
pub fn setters_derive(input: TokenStream) -> TokenStream {
    use syn::{parse_macro_input, DeriveInput};

    let ast = parse_macro_input!(input as DeriveInput);
    let prefix = util::get_prefix(&ast.attrs, "set_", "setters");

    util::gen_for_each_field(&ast, "setter", &prefix, |field,_each| {
        let ident = util::get_field_ident(field);
        let ty = &field.ty;
        quote::quote! {
            (&mut self, #ident: impl ::core::convert::Into<#ty>) {
                self.#ident = #ident.into();
            }
        }
    })
}

#[cfg(feature = "constructor")]
mod constructor;

#[cfg(feature = "constructor")]
#[proc_macro_derive(Constructor, attributes(constructor))]
pub fn constructor_derive(input: TokenStream) -> TokenStream {
    constructor::constructor_derive_impl(input)
}


#[cfg(feature = "as_box")]
#[proc_macro_derive(AsBox)]
pub fn auto_box_derive(input: TokenStream) -> TokenStream {
    use syn::parse_macro_input;

    let ast = parse_macro_input!(input as syn::DeriveInput);
    let ident = &ast.ident;
    let vis = &ast.vis;
    let generics = &ast.generics;
    let stripped = util::get_stripped_generics(generics, false);
    let wher = &generics.where_clause;

    quote::quote! {
        extern crate alloc;

        impl #generics #ident #stripped #wher {
            #vis fn as_box(self) -> ::alloc::boxed::Box<#ident #stripped> {
                std::boxed::Box::new(self)
            }
        }
    }.into()
}

#[cfg(feature = "into_enum")]
#[proc_macro_derive(IntoEnum, attributes(into_enum))]
pub fn wrap_enum_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let ident = &ast.ident;
    let generics = &ast.generics;
    let stripped = util::get_stripped_generics(generics, false);
    let wher = &generics.where_clause;

    let mut enum_name = None;
    let mut field_name = ident.clone();

    for attr in ast.attrs {
        if let Meta::List(MetaList { ref tokens, .. }) = attr.meta {
            let mut tokens = tokens.clone().into_iter();

            loop {
                let Some(key) = tokens.next() else { break };
                let TokenTree::Ident(ref name) = key else { panic!("Expected ident, found {key:#?}") };
                match tokens.next().unwrap() {
                    TokenTree::Punct(ref p) => assert_eq!(p.as_char(), '='),
                    tt => panic!("Expected '=', found: {tt}"),
                }
                let val = tokens.next().unwrap();
                let TokenTree::Ident(ref val) = val else { panic!("Expected ident, found {val:#?}") };

                if name == "enum_name" {
                    enum_name = Some(val.clone());
                }
                if name == "field" {
                    field_name = val.clone();
                }

                if let Some(key) = tokens.next() {
                    let TokenTree::Punct(ref name) = key else { panic!("Expected ',', found {key:#?}") };
                    if name.as_char() == ',' {
                        continue;
                    } else {
                        panic!()
                    }
                }
            }
        }
    }

    let enum_name = enum_name.unwrap();

    quote::quote! {
        impl #generics From<#ident #stripped> for #enum_name #wher {
            fn from(value: #ident #stripped) -> Self {
                #enum_name :: #field_name (value)
            }
        }
    }.into()
}