Skip to content

Commit

Permalink
Explicitely create UninitializedField errors
Browse files Browse the repository at this point in the history
  • Loading branch information
andy128k committed Jan 13, 2021
1 parent ee13c51 commit d686cda
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 115 deletions.
44 changes: 18 additions & 26 deletions derive_builder_core/src/build_method.rs
@@ -1,10 +1,10 @@
use crate::macro_options::FieldWithDefaults;
use doc_comment_from;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use syn;
use Block;
use BuilderPattern;
use Initializer;
use DEFAULT_STRUCT_NAME;

/// Initializer for the struct fields in the build method, implementing
Expand Down Expand Up @@ -52,8 +52,8 @@ pub struct BuildMethod<'a> {
pub target_ty_generics: Option<syn::TypeGenerics<'a>>,
/// Type of error.
pub error_ty: syn::Ident,
/// Field initializers for the target type.
pub initializers: Vec<TokenStream>,
/// Fields for the target type.
pub fields: Vec<FieldWithDefaults<'a>>,
/// Doc-comment of the builder struct.
pub doc_comment: Option<syn::Attribute>,
/// Default value for the whole struct.
Expand All @@ -71,7 +71,13 @@ impl<'a> ToTokens for BuildMethod<'a> {
let vis = &self.visibility;
let target_ty = &self.target_ty;
let target_ty_generics = &self.target_ty_generics;
let initializers = &self.initializers;
let error_ty = &self.error_ty;
let error_constructor = quote!(#error_ty::UninitializedField);
let initializers = &self
.fields
.iter()
.map(|field| field.as_initializer(&error_constructor))
.collect::<Vec<_>>();
let self_param = match self.pattern {
BuilderPattern::Owned => quote!(self),
BuilderPattern::Mutable | BuilderPattern::Immutable => quote!(&self),
Expand All @@ -82,7 +88,6 @@ impl<'a> ToTokens for BuildMethod<'a> {
quote!(let #ident: #target_ty #target_ty_generics = #default_expr;)
});
let validate_fn = self.validate_fn.as_ref().map(|vfn| quote!(#vfn(&self)?;));
let error_ty = &self.error_ty;

if self.enabled {
tokens.append_all(quote!(
Expand All @@ -108,13 +113,9 @@ impl<'a> BuildMethod<'a> {
self
}

/// Populate the `BuildMethod` with appropriate initializers of the
/// underlying struct.
///
/// For each struct field this must be called with the appropriate
/// initializer.
pub fn push_initializer(&mut self, init: Initializer) -> &mut Self {
self.initializers.push(quote!(#init));
/// Set fields for this item.
pub fn fields(&mut self, fields: &[FieldWithDefaults<'a>]) -> &mut Self {
self.fields = fields.to_vec();
self
}
}
Expand All @@ -133,7 +134,7 @@ macro_rules! default_build_method {
target_ty: &syn::Ident::new("Foo", ::proc_macro2::Span::call_site()),
target_ty_generics: None,
error_ty: syn::Ident::new("FooBuilderError", ::proc_macro2::Span::call_site()),
initializers: vec![quote!(foo: self.foo,)],
fields: vec![],
doc_comment: None,
default_struct: None,
validate_fn: None,
Expand All @@ -155,9 +156,7 @@ mod tests {
quote!(#build_method).to_string(),
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
Ok(Foo {
foo: self.foo,
})
Ok(Foo {})
}
)
.to_string()
Expand All @@ -175,9 +174,7 @@ mod tests {
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
let __default: Foo = { Default::default() };
Ok(Foo {
foo: self.foo,
})
Ok(Foo {})
}
)
.to_string()
Expand All @@ -204,9 +201,7 @@ mod tests {
quote!(#build_method).to_string(),
quote!(
pub fn finish(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
Ok(Foo {
foo: self.foo,
})
Ok(Foo {})
}
)
.to_string()
Expand All @@ -227,10 +222,7 @@ mod tests {
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
IpsumBuilder::validate(&self)?;

Ok(Foo {
foo: self.foo,
})
Ok(Foo {})
}
)
.to_string()
Expand Down
90 changes: 30 additions & 60 deletions derive_builder_core/src/builder.rs
Expand Up @@ -51,12 +51,6 @@ use Setter;
/// ValidationError(String),
/// }
///
/// impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
/// fn from(s: &'static str) -> Self {
/// Self::UninitializedField(s)
/// }
/// }
///
/// impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
/// fn from(s: String) -> Self {
/// Self::ValidationError(s)
Expand Down Expand Up @@ -196,15 +190,11 @@ impl<'a> ToTokens for Builder<'a> {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for #builder_error_ident {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for #builder_error_ident {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for #builder_error_ident
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down Expand Up @@ -357,15 +347,11 @@ mod tests {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for FooBuilderError
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down Expand Up @@ -446,15 +432,11 @@ mod tests {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for FooBuilderError
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down Expand Up @@ -535,15 +517,11 @@ mod tests {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for FooBuilderError
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down Expand Up @@ -627,15 +605,11 @@ mod tests {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for FooBuilderError
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down Expand Up @@ -718,15 +692,11 @@ mod tests {
ValidationError(String),
}

impl ::derive_builder::export::core::convert::From<&'static str> for FooBuilderError {
fn from(s: &'static str) -> Self {
Self::UninitializedField(s)
}
}

impl ::derive_builder::export::core::convert::From<String> for FooBuilderError {
fn from(s: String) -> Self {
Self::ValidationError(s)
impl<E> ::derive_builder::export::core::convert::From<E> for FooBuilderError
where E: ::derive_builder::export::core::convert::AsRef<str>
{
fn from(s: E) -> Self {
Self::ValidationError(s.as_ref().to_string())
}
}

Expand Down
41 changes: 24 additions & 17 deletions derive_builder_core/src/initializer.rs
Expand Up @@ -48,6 +48,8 @@ pub struct Initializer<'a> {
pub default_value: Option<Block>,
/// Whether the build_method defines a default struct.
pub use_default_struct: bool,
/// Error constructor
pub error_constructor: &'a TokenStream,
}

impl<'a> ToTokens for Initializer<'a> {
Expand Down Expand Up @@ -90,7 +92,10 @@ impl<'a> Initializer<'a> {
if self.use_default_struct {
MatchNone::UseDefaultStructField(self.field_ident)
} else {
MatchNone::ReturnError(self.field_ident.to_string())
MatchNone::ReturnError {
field_ident: self.field_ident,
error_constructor: self.error_constructor,
}
}
}
}
Expand Down Expand Up @@ -118,7 +123,10 @@ enum MatchNone<'a> {
/// The default struct must be in scope in the build_method.
UseDefaultStructField(&'a syn::Ident),
/// Inner value must be the field name
ReturnError(String),
ReturnError {
field_ident: &'a syn::Ident,
error_constructor: &'a TokenStream,
},
}

impl<'a> ToTokens for MatchNone<'a> {
Expand All @@ -133,9 +141,15 @@ impl<'a> ToTokens for MatchNone<'a> {
None => #struct_ident.#field_ident
))
}
MatchNone::ReturnError(ref err) => tokens.append_all(quote!(
None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(#err))
)),
MatchNone::ReturnError {
ref field_ident,
ref error_constructor,
} => {
let field_name = field_ident.to_string();
tokens.append_all(quote!(
None => return ::derive_builder::export::core::result::Result::Err(#error_constructor(#field_name))
))
}
}
}
}
Expand Down Expand Up @@ -171,6 +185,7 @@ macro_rules! default_initializer {
builder_pattern: BuilderPattern::Mutable,
default_value: None,
use_default_struct: false,
error_constructor: &syn::parse_str("FooError::UninitializedField").unwrap(),
}
};
}
Expand All @@ -190,9 +205,7 @@ mod tests {
quote!(
foo: match self.foo {
Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
"foo"
)),
None => return ::derive_builder::export::core::result::Result::Err(FooError::UninitializedField("foo")),
},
)
.to_string()
Expand All @@ -209,9 +222,7 @@ mod tests {
quote!(
foo: match self.foo {
Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
"foo"
)),
None => return ::derive_builder::export::core::result::Result::Err(FooError::UninitializedField("foo")),
},
)
.to_string()
Expand All @@ -228,9 +239,7 @@ mod tests {
quote!(
foo: match self.foo {
Some(value) => value,
None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
"foo"
)),
None => return ::derive_builder::export::core::result::Result::Err(FooError::UninitializedField("foo")),
},
)
.to_string()
Expand Down Expand Up @@ -291,9 +300,7 @@ mod tests {
quote!(
foo: match self.foo {
Some(ref value) => ::derive_builder::export::core::clone::Clone::clone(value),
None => return ::derive_builder::export::core::result::Result::Err(::derive_builder::export::core::convert::Into::into(
"foo"
)),
None => return ::derive_builder::export::core::result::Result::Err(FooError::UninitializedField("foo")),
},
)
.to_string()
Expand Down
16 changes: 8 additions & 8 deletions derive_builder_core/src/lib.rs
Expand Up @@ -64,24 +64,24 @@ pub fn builder_for_struct(ast: syn::DeriveInput) -> proc_macro2::TokenStream {
}
};

let mut builder = opts.as_builder();
let mut build_fn = opts.as_build_method();
let fields = opts.fields().collect::<Vec<_>>();

builder.doc_comment(format!(
include_str!("doc_tpl/builder_struct.md"),
struct_name = ast.ident
));
let mut build_fn = opts.as_build_method();
build_fn.doc_comment(format!(
include_str!("doc_tpl/builder_method.md"),
struct_name = ast.ident
));
build_fn.fields(&fields);

let mut builder = opts.as_builder();
builder.doc_comment(format!(
include_str!("doc_tpl/builder_struct.md"),
struct_name = ast.ident
));
for field in opts.fields() {
builder.push_field(field.as_builder_field());
builder.push_setter_fn(field.as_setter());
build_fn.push_initializer(field.as_initializer());
}

builder.push_build_fn(build_fn);

quote!(#builder)
Expand Down

0 comments on commit d686cda

Please sign in to comment.