Skip to content

Commit

Permalink
Add Optional
Browse files Browse the repository at this point in the history
  • Loading branch information
mo8it committed Jun 11, 2023
1 parent 1a05d73 commit b9ca287
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 51 deletions.
2 changes: 1 addition & 1 deletion examples/complicate_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod scope {
// We can use `cargo expand` to show code expanded by `TypedBuilder`,
// copy the generated `__build` method, and modify the content of the build method.
#[allow(non_camel_case_types)]
impl<__z: FooBuilder_Optional<i32>, __y: FooBuilder_Optional<Option<i32>>> FooBuilder<((i32,), __y, __z)> {
impl<__z: typed_builder::Optional<i32>, __y: typed_builder::Optional<Option<i32>>> FooBuilder<((i32,), __y, __z)> {
pub fn build(self) -> Bar {
let foo = self.__build();
Bar {
Expand Down
17 changes: 17 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,23 @@
/// when the setter is called.
pub use typed_builder_macro::TypedBuilder;

#[doc(hidden)]
pub trait Optional<T> {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
}

impl<T> Optional<T> for () {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
default()
}
}

impl<T> Optional<T> for (T,) {
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
self.0
}
}

// It'd be nice for the compilation tests to live in tests/ with the rest, but short of pulling in
// some other test runner for that purpose (e.g. compiletest_rs), rustdoc compile_fail in this
// crate is all we can use.
Expand Down
2 changes: 0 additions & 2 deletions typed-builder-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
syn::Fields::Named(fields) => {
let struct_info = struct_info::StructInfo::new(ast, fields.named.iter())?;
let builder_creation = struct_info.builder_creation_impl()?;
let conversion_helper = struct_info.conversion_helper_impl();
let fields = struct_info
.included_fields()
.map(|f| struct_info.field_impl(f))
Expand All @@ -34,7 +33,6 @@ fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {

quote! {
#builder_creation
#conversion_helper
#fields
#(#required_fields)*
#build_method
Expand Down
66 changes: 24 additions & 42 deletions typed-builder-macro/src/struct_info.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use proc_macro2::TokenStream;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::parse::Error;

use crate::field_info::{FieldBuilderAttr, FieldInfo};
use crate::util::{
apply_subsections, empty_type, empty_type_tuple, expr_to_single_string, first_visibility, make_punctuated_single,
modify_types_generics_hack, path_to_single_string, public_visibility, strip_raw_ident_prefix, type_tuple,
apply_subsections, empty_type, empty_type_tuple, expr_to_single_string, first_visibility, modify_types_generics_hack,
path_to_single_string, public_visibility, strip_raw_ident_prefix, type_tuple,
};

#[derive(Debug)]
Expand All @@ -17,7 +17,6 @@ pub struct StructInfo<'a> {

pub builder_attr: TypeBuilderAttr<'a>,
pub builder_name: syn::Ident,
pub conversion_helper_trait_name: syn::Ident,
pub core: syn::Ident,
}

Expand All @@ -43,7 +42,6 @@ impl<'a> StructInfo<'a> {
.collect::<Result<_, _>>()?,
builder_attr,
builder_name: syn::Ident::new(&builder_name, proc_macro2::Span::call_site()),
conversion_helper_trait_name: syn::Ident::new(&format!("{}_Optional", builder_name), proc_macro2::Span::call_site()),
core: syn::Ident::new(&format!("{}_core", builder_name), proc_macro2::Span::call_site()),
})
}
Expand Down Expand Up @@ -167,31 +165,6 @@ impl<'a> StructInfo<'a> {
})
}

// TODO: once the proc-macro crate limitation is lifted, make this an util trait of this
// crate.
pub fn conversion_helper_impl(&self) -> TokenStream {
let trait_name = &self.conversion_helper_trait_name;
quote! {
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub trait #trait_name<T> {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
}

impl<T> #trait_name<T> for () {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
default()
}
}

impl<T> #trait_name<T> for (T,) {
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
self.0
}
}
}
}

pub fn field_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
let StructInfo { ref builder_name, .. } = *self;

Expand Down Expand Up @@ -435,16 +408,26 @@ impl<'a> StructInfo<'a> {
paren_token: None,
lifetimes: None,
modifier: syn::TraitBoundModifier::None,
path: syn::PathSegment {
ident: self.conversion_helper_trait_name.clone(),
arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
colon2_token: None,
lt_token: Default::default(),
args: make_punctuated_single(syn::GenericArgument::Type(field.ty.clone())),
gt_token: Default::default(),
}),
}
.into(),
path: syn::Path {
leading_colon: Some(syn::token::PathSep::default()),
segments: [
syn::PathSegment {
ident: Ident::new("typed_builder", Span::call_site()),
arguments: syn::PathArguments::None,
},
syn::PathSegment {
ident: Ident::new("Optional", Span::call_site()),
arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
colon2_token: None,
lt_token: Default::default(),
args: [syn::GenericArgument::Type(field.ty.clone())].into_iter().collect(),
gt_token: Default::default(),
}),
},
]
.into_iter()
.collect(),
},
};
let mut generic_param: syn::TypeParam = field.generic_ident.clone().into();
generic_param.bounds.push(trait_ref.into());
Expand Down Expand Up @@ -472,7 +455,6 @@ impl<'a> StructInfo<'a> {

let descructuring = self.included_fields().map(|f| f.name);

let helper_trait_name = &self.conversion_helper_trait_name;
// The default of a field can refer to earlier-defined fields, which we handle by
// writing out a bunch of `let` statements first, which can each refer to earlier ones.
// This means that field ordering may actually be significant, which isn't ideal. We could
Expand All @@ -484,7 +466,7 @@ impl<'a> StructInfo<'a> {
if field.builder_attr.setter.skip.is_some() {
quote!(let #name = #default;)
} else {
quote!(let #name = #helper_trait_name::into_value(#name, || #default);)
quote!(let #name = ::typed_builder::Optional::into_value(#name, || #default);)
}
} else {
quote!(let #name = #name.0;)
Expand Down
6 changes: 0 additions & 6 deletions typed-builder-macro/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,6 @@ pub fn empty_type_tuple() -> syn::TypeTuple {
}
}

pub fn make_punctuated_single<T, P: Default>(value: T) -> syn::punctuated::Punctuated<T, P> {
let mut punctuated = syn::punctuated::Punctuated::new();
punctuated.push(value);
punctuated
}

pub fn modify_types_generics_hack<F>(ty_generics: &syn::TypeGenerics, mut mutator: F) -> syn::AngleBracketedGenericArguments
where
F: FnMut(&mut syn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>),
Expand Down

0 comments on commit b9ca287

Please sign in to comment.