Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ struct Foo {
z: i32,
}

#[derive(PartialEq, TypedBuilder)]
struct Bar(
i32,
// #[default] without parameter - use the type's default
#[default]
Option<i32>,

// Or you can set the default(encoded as string)
#[default="20"]
i32,
);

#[derive(PartialEq, TypedBuilder)]
struct Baz;

fn main() {
assert!(
Foo::builder().x(1).y(2).z(3).build()
Expand All @@ -35,4 +50,31 @@ fn main() {

// This will not compile - because we set y twice:
// Foo::builder().x(1).y(2).y(3);


assert!(
Bar::builder()._0(1)._1(2)._2(3).build()
== Bar(1, Some(2), 3));

// Change the order of construction:
assert!(
Bar::builder()._2(1)._0(2)._1(3).build()
== Bar(2, Some(3), 1));

// Optional fields are optional:
assert!(
Bar::builder()._0(1).build()
== Bar(1, None, 20));

// This will not compile - because we did not set `0`:
// Bar::builder().build();

// This will not compile - because we set `1` twice:
// Bar::builder()._0(1)._1(2)._1(3);


assert!(
Baz::builder().build()
== Baz
);
}
24 changes: 13 additions & 11 deletions src/field_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,22 @@ use util::{make_identifier, map_only_one};

pub struct FieldInfo<'a> {
pub ordinal: usize,
pub name: &'a syn::Ident,
pub name: ::std::borrow::Cow<'a, syn::Ident>,
pub generic_ident: syn::Ident,
pub ty: &'a syn::Ty,
pub default: Option<Tokens>,
}

impl<'a> FieldInfo<'a> {
pub fn new(ordinal: usize, field: &syn::Field) -> FieldInfo {
if let Some(ref name) = field.ident {
FieldInfo {
ordinal: ordinal,
name: &name,
generic_ident: make_identifier("genericType", name),
ty: &field.ty,
default: Self::find_field_default(field).unwrap_or_else(|f| panic!("Field {}: {}", name, f)),
}
} else {
panic!("Nameless field in struct");
let name = field.ident.as_ref().map(::std::borrow::Cow::Borrowed).unwrap_or_else(|| ::std::borrow::Cow::Owned(format!("_{}", ordinal).into()));

FieldInfo {
ordinal: ordinal,
generic_ident: make_identifier("genericType", &name),
default: Self::find_field_default(field).unwrap_or_else(|f| panic!("Field {}: {}", name, f)),
name,
ty: &field.ty,
}
}

Expand All @@ -44,6 +42,10 @@ impl<'a> FieldInfo<'a> {
})
}

pub fn name(&self) -> &syn::Ident {
&self.name
}

pub fn generic_ty_param(&self) -> syn::TyParam {
syn::TyParam::from(self.generic_ident.clone())
}
Expand Down
14 changes: 9 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,16 @@ pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
}

fn impl_my_derive(ast: &syn::DeriveInput) -> quote::Tokens {

match ast.body {
syn::Body::Struct(syn::VariantData::Struct(ref body)) => {
let struct_info = struct_info::StructInfo::new(&ast, body);
syn::Body::Struct(ref body) => {
let kind = match *body {
syn::VariantData::Struct(_) => struct_info::StructKind::Struct,
syn::VariantData::Tuple(_) => struct_info::StructKind::Tuple,
syn::VariantData::Unit => struct_info::StructKind::Unit,
};


let struct_info = struct_info::StructInfo::new(&ast, body.fields(), kind);
let builder_creation = struct_info.builder_creation_impl();
let conversion_helper = struct_info.conversion_helper_impl();
let fields = struct_info.fields.iter().map(|f| struct_info.field_impl(f));
Expand All @@ -90,8 +96,6 @@ fn impl_my_derive(ast: &syn::DeriveInput) -> quote::Tokens {
#build_method
}
},
syn::Body::Struct(syn::VariantData::Unit) => panic!("SmartBuilder is not supported for unit types"),
syn::Body::Struct(syn::VariantData::Tuple(_)) => panic!("SmartBuilder is not supported for tuples"),
syn::Body::Enum(_) => panic!("SmartBuilder is not supported for enums"),
}
}
Expand Down
86 changes: 65 additions & 21 deletions src/struct_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@ use quote::Tokens;
use field_info::FieldInfo;
use util::make_identifier;

pub enum StructKind {
Struct,
Tuple,
Unit,
}

pub struct StructInfo<'a> {
pub vis: &'a syn::Visibility,
pub name: &'a syn::Ident,
pub generics: &'a syn::Generics,
pub fields: Vec<FieldInfo<'a>>,
pub kind: StructKind,

pub builder_name: syn::Ident,
pub conversion_helper_trait_name: syn::Ident,
pub conversion_helper_method_name: syn::Ident,
}

impl<'a> StructInfo<'a> {
pub fn new(ast: &'a syn::DeriveInput, fields: &'a [syn::Field]) -> StructInfo<'a> {
pub fn new(ast: &'a syn::DeriveInput, fields: &'a [syn::Field], kind: StructKind) -> StructInfo<'a> {
StructInfo {
vis: &ast.vis,
name: &ast.ident,
generics: &ast.generics,
fields: fields.iter().enumerate().map(|(i, f)| FieldInfo::new(i, f)).collect(),
kind,
builder_name: make_identifier("BuilderFor", &ast.ident),
conversion_helper_trait_name: make_identifier("conversionHelperTrait", &ast.ident),
conversion_helper_method_name: make_identifier("conversionHelperMethod", &ast.ident),
Expand All @@ -36,13 +44,13 @@ impl<'a> StructInfo<'a> {
}

pub fn builder_creation_impl(&self) -> Tokens {
let _ = self.modify_generics(|g| g.ty_params.push(self.fields[0].generic_ty_param()));
let _ = self.modify_generics(|g| self.fields.iter().for_each(|f| g.ty_params.push(f.generic_ty_param())));
let init_empties = {
let names = self.fields.iter().map(|f| f.name);
let names = self.fields.iter().map(|f| f.name());
quote!(#( #names: () ),*)
};
let builder_generics = {
let names = self.fields.iter().map(|f| f.name);
let names = self.fields.iter().map(|f| f.name());
let generic_idents = self.fields.iter().map(|f| &f.generic_ident);
quote!(#( #names: #generic_idents ),*)
};
Expand Down Expand Up @@ -105,7 +113,7 @@ impl<'a> StructInfo<'a> {
} else {
write!(&mut result, ", ").unwrap();
}
write!(&mut result, "`.{}(...)`", field.name).unwrap();
write!(&mut result, "`.{}(...)`", field.name()).unwrap();
if field.default.is_some() {
write!(&mut result, "(optional)").unwrap();
}
Expand Down Expand Up @@ -144,10 +152,10 @@ impl<'a> StructInfo<'a> {
pub fn field_impl(&self, field: &FieldInfo) -> Tokens {
let ref builder_name = self.builder_name;
let other_fields_name =
self.fields.iter().filter(|f| f.ordinal != field.ordinal).map(|f| f.name);
// not really "value", since we just use to self.name - but close enough.
self.fields.iter().filter(|f| f.ordinal != field.ordinal).map(|f| f.name());
// not really "value", since we just use to self.name() - but close enough.
let other_fields_value =
self.fields.iter().filter(|f| f.ordinal != field.ordinal).map(|f| f.name);
self.fields.iter().filter(|f| f.ordinal != field.ordinal).map(|f| f.name());
let &FieldInfo { name: ref field_name, ty: ref field_type, ref generic_ident, .. } = field;
let generics = self.modify_generics(|g| {
for f in self.fields.iter() {
Expand Down Expand Up @@ -232,21 +240,57 @@ impl<'a> StructInfo<'a> {
let (_, ty_generics, where_clause) = self.generics.split_for_impl();

let ref helper_trait_method_name = self.conversion_helper_method_name;
let assignments = self.fields.iter().map(|field| {
let ref name = field.name;
if let Some(ref default) = field.default {
quote!(#name: self.#name.#helper_trait_method_name(#default))
} else {
quote!(#name: self.#name.0)

match self.kind {
StructKind::Struct => {
let assignments = self.fields.iter().map(|field| {
let ref name = field.name();
if let Some(ref default) = field.default {
quote!(#name: self.#name.#helper_trait_method_name(#default))
} else {
quote!(#name: self.#name.0)
}
});

quote! {
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl #impl_generics #builder_name #modified_ty_generics #where_clause {
pub fn build(self) -> #name #ty_generics {
#name {
#( #assignments ),*
}
}
}
}
}
});
StructKind::Tuple => {
let assignments = self.fields.iter().map(|field| {
let ref name = field.name();
if let Some(ref default) = field.default {
quote!(self.#name.#helper_trait_method_name(#default))
} else {
quote!(self.#name.0)
}
});

quote! {
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl #impl_generics #builder_name #modified_ty_generics #where_clause {
pub fn build(self) -> #name #ty_generics {
#name {
#( #assignments ),*
quote! {
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl #impl_generics #builder_name #modified_ty_generics #where_clause {
pub fn build(self) -> #name #ty_generics {
#name (
#( #assignments ),*
)
}
}
}
}
StructKind::Unit => {
quote! {
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl #impl_generics #builder_name #modified_ty_generics #where_clause {
pub fn build(self) -> #name #ty_generics {
#name
}
}
}
}
Expand Down