use proc_macro::TokenStream;
use quote::{quote, format_ident};
use syn::{DeriveInput, parse_macro_input, Data, Fields, Type};
use proc_macro2::TokenStream as TokenStream2;
pub fn derive_builder(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input_ident = input.ident; let ident_builder = format_ident!("{}Builder", input_ident.to_string());
if let Data::Struct(r) = input.data { let fields = r.fields;
let builder_fields = map_fields(&fields, &mut |(ident, ty)| {
quote!(
#ident: Option<#ty>,
)
});
let builder_set_fields = map_fields(&fields, &mut |(ident, ty)| {
quote!(
pub fn #ident(mut self, value: #ty) -> Self {
self.#ident = Some(value);
self
}
)
});
let builder_lets = map_fields(&fields, &mut |(ident, _)| {
quote!(
let #ident = self.#ident.ok_or(format!(
"field {:?} not set yet", stringify!(#ident),
))?;
)
});
let builder_fields_values = map_fields(&fields, &mut |(ident, _)| {
quote!(
#ident,
)
});
quote!(
impl #input_ident {
pub fn builder() -> #ident_builder {
#ident_builder::default()
}
}
#[derive(Default)]
pub struct #ident_builder {
#builder_fields
}
impl #ident_builder {
#builder_set_fields
pub fn build(self) -> Result<#input_ident, String> {
#builder_lets
Ok(#input_ident{ #builder_fields_values })
}
}
).into()
} else {
quote!().into()
}
}
fn map_fields<F>(fields: &Fields, mapper:&mut F) -> TokenStream2
where
F: FnMut((&Option<proc_macro2::Ident> , &Type)) -> TokenStream2,
{
let fs = fields.iter().map(|field| mapper((&field.ident ,&field.ty)) );
let stream2 = TokenStream2::from_iter(fs);
stream2
}