use quote::{ToTokens, Tokens};
use syn::punctuated::Punctuated;
use syn::{self, Ident, TraitBound, TraitBoundModifier, TypeParamBound};
use doc_comment::doc_comment_from;
use Bindings;
use BuildMethod;
use BuilderField;
use BuilderPattern;
use DeprecationNotes;
use Setter;
#[derive(Debug)]
pub struct Builder<'a> {
pub enabled: bool,
pub ident: syn::Ident,
pub pattern: BuilderPattern,
pub derives: &'a [syn::Ident],
pub generics: Option<&'a syn::Generics>,
pub visibility: syn::Visibility,
pub fields: Vec<Tokens>,
pub functions: Vec<Tokens>,
pub must_derive_clone: bool,
pub doc_comment: Option<syn::Attribute>,
pub deprecation_notes: DeprecationNotes,
pub bindings: Bindings,
}
impl<'a> ToTokens for Builder<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
if self.enabled {
trace!("Deriving builder `{}`.", self.ident);
let builder_vis = &self.visibility;
let builder_ident = self.ident;
let bounded_generics = self.compute_impl_bounds();
let (impl_generics, _, _) = bounded_generics.split_for_impl();
let (struct_generics, ty_generics, where_clause) = self
.generics
.map(syn::Generics::split_for_impl)
.map(|(i, t, w)| (Some(i), Some(t), Some(w)))
.unwrap_or((None, None, None));
let builder_fields = &self.fields;
let functions = &self.functions;
let derived_traits = {
let default_trait: Ident = parse_quote!(Default);
let clone_trait: Ident = parse_quote!(Clone);
let mut traits: Punctuated<&Ident, Token![,]> = Default::default();
traits.push(&default_trait);
if self.must_derive_clone {
traits.push(&clone_trait);
}
traits.extend(self.derives);
quote!(#traits)
};
let builder_doc_comment = &self.doc_comment;
let deprecation_notes = &self.deprecation_notes.as_item();
debug!(
"ty_generics={:?}, where_clause={:?}, struct_generics={:?}",
ty_generics, where_clause, struct_generics
);
tokens.append_all(quote!(
#[derive(#derived_traits)]
#builder_doc_comment
#builder_vis struct #builder_ident #struct_generics #where_clause {
#(#builder_fields)*
}
#[allow(dead_code)]
impl #impl_generics #builder_ident #ty_generics #where_clause {
#(#functions)*
#deprecation_notes
}
));
} else {
trace!("Skipping builder `{}`.", self.ident);
}
}
}
impl<'a> Builder<'a> {
pub fn doc_comment(&mut self, s: String) -> &mut Self {
self.doc_comment = Some(doc_comment_from(s));
self
}
pub fn push_field(&mut self, f: BuilderField) -> &mut Self {
self.fields.push(quote!(#f));
self
}
pub fn push_setter_fn(&mut self, f: Setter) -> &mut Self {
self.functions.push(quote!(#f));
self
}
pub fn push_build_fn(&mut self, f: BuildMethod) -> &mut Self {
self.functions.push(quote!(#f));
self
}
fn compute_impl_bounds(&self) -> syn::Generics {
if let Some(type_gen) = self.generics {
let mut generics = type_gen.clone();
if !self.pattern.requires_clone() || type_gen.type_params().next().is_none() {
return generics;
}
let clone_bound = TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: self.bindings.clone_trait(),
});
for typ in generics.type_params_mut() {
typ.bounds.push(clone_bound.clone());
}
generics
} else {
Default::default()
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! default_builder {
() => {
Builder {
enabled: true,
ident: syn::Ident::from("FooBuilder"),
pattern: Default::default(),
derives: &vec![],
generics: None,
visibility: syn::parse_str("pub").unwrap(),
fields: vec![quote!(foo: u32,)],
functions: vec![quote!(fn bar() -> { unimplemented!() })],
must_derive_clone: true,
doc_comment: None,
deprecation_notes: DeprecationNotes::default(),
bindings: Default::default(),
}
};
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn simple() {
let builder = default_builder!();
assert_eq!(
quote!(#builder),
quote!(
#[derive(Default, Clone)]
pub struct FooBuilder {
foo: u32,
}
#[allow(dead_code)]
impl FooBuilder {
fn bar () -> {
unimplemented!()
}
}
)
);
}
#[cfg_attr(rustfmt, rustfmt_skip)]
#[test]
fn generic() {
let ast: syn::DeriveInput = syn::parse_str(stringify!(
struct Lorem<'a, T: Debug> where T: PartialEq { }
)).expect("Couldn't parse item");
let generics = ast.generics;
let mut builder = default_builder!();
builder.generics = Some(&generics);
assert_eq!(
quote!(#builder),
quote!(
#[derive(Default, Clone)]
pub struct FooBuilder<'a, T: Debug> where T: PartialEq {
foo: u32,
}
#[allow(dead_code)]
impl<'a, T: Debug + ::std::clone::Clone> FooBuilder<'a, T> where T: PartialEq {
fn bar() -> {
unimplemented!()
}
}
)
);
}
#[cfg_attr(rustfmt, rustfmt_skip)]
#[test]
fn generic_reference() {
let ast: syn::DeriveInput = syn::parse_str(stringify!(
struct Lorem<'a, T: 'a + Default> where T: PartialEq{ }
)).expect("Couldn't parse item");
let generics = ast.generics;
let mut builder = default_builder!();
builder.generics = Some(&generics);
assert_eq!(
quote!(#builder),
quote!(
#[derive(Default, Clone)]
pub struct FooBuilder<'a, T: 'a + Default> where T: PartialEq {
foo: u32,
}
#[allow(dead_code)]
impl<'a, T: 'a + Default + ::std::clone::Clone> FooBuilder<'a, T> where T: PartialEq {
fn bar() -> {
unimplemented!()
}
}
)
);
}
#[cfg_attr(rustfmt, rustfmt_skip)]
#[test]
fn owned_generic() {
let ast: syn::DeriveInput = syn::parse_str(stringify!(
struct Lorem<'a, T: Debug> where T: PartialEq { }
)).expect("Couldn't parse item");
let generics = ast.generics;
let mut builder = default_builder!();
builder.generics = Some(&generics);
builder.pattern = BuilderPattern::Owned;
builder.must_derive_clone = false;
assert_eq!(
quote!(#builder),
quote!(
#[derive(Default)]
pub struct FooBuilder<'a, T: Debug> where T: PartialEq {
foo: u32,
}
#[allow(dead_code)]
impl<'a, T: Debug> FooBuilder<'a, T> where T: PartialEq {
fn bar() -> {
unimplemented!()
}
}
)
);
}
#[test]
fn disabled() {
let mut builder = default_builder!();
builder.enabled = false;
assert_eq!(quote!(#builder), quote!());
}
#[test]
fn add_derives() {
let derives = vec![syn::Ident::from("Serialize")];
let mut builder = default_builder!();
builder.derives = &derives;
assert_eq!(
quote!(#builder),
quote!(
#[derive(Default, Clone, Serialize)]
pub struct FooBuilder {
foo: u32,
}
#[allow(dead_code)]
impl FooBuilder {
fn bar () -> {
unimplemented!()
}
}
)
);
}
}