From 64d73044a50cf1d70ffe4e0c1cc9454958f93b4e Mon Sep 17 00:00:00 2001 From: "Ifiok Jr." Date: Wed, 21 Aug 2024 07:23:43 +0100 Subject: [PATCH 1/4] feat: add `strip_option(fallback = "field_opt")` --- CHANGELOG.md | 3 + src/lib.rs | 44 +++++++++++- tests/tests.rs | 24 +++++++ typed-builder-macro/src/field_info.rs | 93 ++++++++++++++++++++++++-- typed-builder-macro/src/struct_info.rs | 59 ++++++++++++---- 5 files changed, 204 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 694c018e..047b1905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Add `#[builder(setter(strip_option(fallback = "field_opt")))]` to add a fallback unstripped method to the builder struct. + ## 0.19.1 - 2024-07-14 ### Fixed - Fix mutators for generic fields (see issue #149) diff --git a/src/lib.rs b/src/lib.rs index 2a9a8588..c5819b50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,6 +165,12 @@ use core::ops::FnOnce; /// one cannot set the field to `None` with the setter - so the only way to get it to be `None` /// is by using `#[builder(default)]` and not calling the field's setter. /// +/// - `strip_option(fallback = "field_opt")`: for `Option<...>` fields only. As above this +/// still wraps the argument with `Some(...)`. The name given to the fallback method adds +/// another method to the builder without wrapping the argument in `Some`. This is useful +/// when the codebase sometimes needs to pass in optional values. You can now call with +/// `field_opt(Some(...))` instead of `field(...)`. +/// /// - `strip_bool`: for `bool` fields only, this makes the setter receive no arguments and simply /// set the field's value to `true`. When used, the `default` is automatically set to `false`. /// @@ -359,5 +365,41 @@ impl Optional for (T,) { /// /// #[deny(deprecated)] /// Foo::builder().value(42).build(); -///``` +/// ``` +/// +/// Handling invalid property for `strip_option` +/// +/// ```compile_fail +/// use typed_builder::TypedBuilder; +/// +/// #[derive(TypedBuilder)] +/// struct Foo { +/// #[builder(setter(strip_option(invalid_field = "should_fail")))] +/// value: Option, +/// } +/// ``` +/// +/// Handling multiple properties for `strip_option` +/// +/// ```compile_fail +/// use typed_builder::TypedBuilder; +/// +/// #[derive(TypedBuilder)] +/// struct Foo { +/// #[builder(setter(strip_option(fallback= "value_opt", fallback="value_opt2")))] +/// value: Option, +/// } +/// ``` +/// +/// Handling empty properties for `strip_option` +/// +/// ```compile_fail +/// use typed_builder::TypedBuilder; +/// +/// #[derive(TypedBuilder)] +/// struct Foo { +/// #[builder(setter(strip_option()))] +/// value: Option, +/// } +/// ``` fn _compile_fail_tests() {} diff --git a/tests/tests.rs b/tests/tests.rs index 1ab670eb..cf4d8682 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -143,6 +143,30 @@ fn test_into_with_strip_option() { assert!(Foo::builder().x(1_u8).build() == Foo { x: Some(1) }); } +#[test] +fn test_strip_option_with_fallback() { + #[derive(PartialEq, TypedBuilder)] + struct Foo { + #[builder(setter(strip_option(fallback = "x_opt")))] + x: Option, + } + + assert!(Foo::builder().x(1).build() == Foo { x: Some(1) }); + assert!(Foo::builder().x_opt(Some(1)).build() == Foo { x: Some(1) }); +} + +#[test] +fn test_into_with_strip_option_with_fallback() { + #[derive(PartialEq, TypedBuilder)] + struct Foo { + #[builder(setter(into, strip_option(fallback = "x_opt")))] + x: Option, + } + + assert!(Foo::builder().x(1_u8).build() == Foo { x: Some(1) }); + assert!(Foo::builder().x_opt(Some(1)).build() == Foo { x: Some(1) }); +} + #[test] fn test_strip_bool() { #[derive(PartialEq, TypedBuilder)] diff --git a/typed-builder-macro/src/field_info.rs b/typed-builder-macro/src/field_info.rs index 82ddad3c..f5645b57 100644 --- a/typed-builder-macro/src/field_info.rs +++ b/typed-builder-macro/src/field_info.rs @@ -3,7 +3,9 @@ use quote::quote_spanned; use syn::{parse::Error, spanned::Spanned}; use crate::mutator::Mutator; -use crate::util::{expr_to_lit_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix, ApplyMeta, AttrArg}; +use crate::util::{ + expr_to_lit_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix, ApplyMeta, AttrArg, SubAttr, +}; #[derive(Debug)] pub struct FieldInfo<'a> { @@ -127,7 +129,7 @@ pub struct SetterSettings { pub doc: Option, pub skip: Option, pub auto_into: Option, - pub strip_option: Option, + pub strip_option: Option, pub strip_bool: Option, pub transform: Option, pub prefix: Option, @@ -195,7 +197,7 @@ impl<'a> FieldBuilderAttr<'a> { let conflicting_transformations = [ ("transform", self.setter.transform.as_ref().map(|t| &t.span)), - ("strip_option", self.setter.strip_option.as_ref()), + ("strip_option", self.setter.strip_option.as_ref().map(|s| s.span())), ("strip_bool", self.setter.strip_bool.as_ref()), ]; let mut conflicting_transformations = conflicting_transformations @@ -335,7 +337,38 @@ impl ApplyMeta for SetterSettings { } "skip" => expr.apply_flag_to_field(&mut self.skip, "skipped"), "into" => expr.apply_flag_to_field(&mut self.auto_into, "calling into() on the argument"), - "strip_option" => expr.apply_flag_to_field(&mut self.strip_option, "putting the argument in Some(...)"), + "strip_option" => { + let caption = "putting the argument in Some(...)"; + match expr { + AttrArg::Sub(sub) => { + if self.strip_option.is_none() { + self.strip_option = Some(StripOption::from_args(sub)?); + Ok(()) + } else { + Err(Error::new( + sub.span(), + format!("Illegal setting - field is already {caption}"), + )) + } + } + AttrArg::Flag(flag) => { + if self.strip_option.is_none() { + self.strip_option = Some(StripOption::Span(flag.span())); + Ok(()) + } else { + Err(Error::new( + flag.span(), + format!("Illegal setting - field is already {caption}"), + )) + } + } + AttrArg::Not { .. } => { + self.strip_option = None; + Ok(()) + } + _ => Err(expr.incorrect_type()), + } + } "strip_bool" => expr.apply_flag_to_field(&mut self.strip_bool, "zero arguments setter, sets the field to true"), _ => Err(Error::new_spanned( expr.name(), @@ -345,6 +378,58 @@ impl ApplyMeta for SetterSettings { } } +#[derive(Debug, Clone)] +pub enum StripOption { + Span(Span), + WithFallback(Span, String), +} + +impl StripOption { + fn span(&self) -> &Span { + match self { + StripOption::Span(span) => span, + StripOption::WithFallback(span, _) => span, + } + } + + pub fn from_args(sub: SubAttr) -> Result { + let name = sub.name.clone(); + let mut total = 0; + let mut result: Result = Err(Error::new_spanned( + &name, + format!("Parameters required {:?}", name.to_string()), + )); + + for arg in sub.args::()? { + if total > 0 { + result = Err(Error::new_spanned( + arg.name(), + format!("Too many paramters {:?}", arg.name().to_string()), + )); + + continue; + } + + if arg.name().to_string().as_str() != "fallback" { + result = Err(Error::new_spanned( + arg.name(), + format!("Invalid parameter used {:?}", arg.name().to_string()), + )); + continue; + } + + let span = arg.span(); + let string_expr: syn::Expr = arg.key_value().map(|kv| kv.parse_value())??; + let name: String = expr_to_lit_string(&string_expr)?; + result = Ok(Self::WithFallback(span, name)); + + total += 1; + } + + result + } +} + #[derive(Debug, Clone)] pub struct Transform { pub params: Vec<(syn::Pat, syn::Type)>, diff --git a/typed-builder-macro/src/struct_info.rs b/typed-builder-macro/src/struct_info.rs index a68ea1a5..0ceea1a8 100644 --- a/typed-builder-macro/src/struct_info.rs +++ b/typed-builder-macro/src/struct_info.rs @@ -3,7 +3,7 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse::Error, parse_quote, punctuated::Punctuated, GenericArgument, ItemFn, Token}; use crate::builder_attr::{IntoSetting, TypeBuilderAttr}; -use crate::field_info::FieldInfo; +use crate::field_info::{FieldInfo, StripOption}; use crate::mutator::Mutator; use crate::util::{ empty_type, empty_type_tuple, first_visibility, modify_types_generics_hack, phantom_data_for_generics, public_visibility, @@ -202,15 +202,18 @@ impl<'a> StructInfo<'a> { fn field_impl(&self, field: &FieldInfo) -> syn::Result { let StructInfo { ref builder_name, .. } = *self; - let descructuring = self.included_fields().map(|f| { - if f.ordinal == field.ordinal { - quote!(()) - } else { - let name = f.name; - name.to_token_stream() - } - }); - let reconstructing = self.included_fields().map(|f| f.name); + let destructuring = self + .included_fields() + .map(|f| { + if f.ordinal == field.ordinal { + quote!(()) + } else { + let name = f.name; + name.to_token_stream() + } + }) + .collect::>(); + let reconstructing = self.included_fields().map(|f| f.name).collect::>(); let &FieldInfo { name: field_name, @@ -273,13 +276,22 @@ impl<'a> StructInfo<'a> { (arg_type.to_token_stream(), field_name.to_token_stream()) }; + let mut strip_option_fallback: Option<(Ident, TokenStream, TokenStream)> = None; let (param_list, arg_expr) = if field.builder_attr.setter.strip_bool.is_some() { (quote!(), quote!(true)) } else if let Some(transform) = &field.builder_attr.setter.transform { let params = transform.params.iter().map(|(pat, ty)| quote!(#pat: #ty)); let body = &transform.body; (quote!(#(#params),*), quote!({ #body })) - } else if field.builder_attr.setter.strip_option.is_some() { + } else if let Some(ref strip_option) = field.builder_attr.setter.strip_option { + if let StripOption::WithFallback(_, fallback_name) = strip_option { + strip_option_fallback = Some(( + syn::parse_str(fallback_name)?, + quote!(#field_name: #field_type), + quote!(#arg_expr), + )); + } + (quote!(#field_name: #arg_type), quote!(Some(#arg_expr))) } else { (quote!(#field_name: #arg_type), arg_expr) @@ -297,6 +309,24 @@ impl<'a> StructInfo<'a> { let method_name = field.setter_method_name(); + let fallback_method = if let Some((method_name, param_list, arg_expr)) = strip_option_fallback { + Some(quote! { + #deprecated + #doc + #[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)] + pub fn #method_name (self, #param_list) -> #builder_name <#target_generics> { + let #field_name = (#arg_expr,); + let ( #(#destructuring,)* ) = self.fields; + #builder_name { + fields: ( #(#reconstructing,)* ), + phantom: self.phantom, + } + } + }) + } else { + None + }; + Ok(quote! { #[allow(dead_code, non_camel_case_types, missing_docs)] #[automatically_derived] @@ -306,12 +336,13 @@ impl<'a> StructInfo<'a> { #[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)] pub fn #method_name (self, #param_list) -> #builder_name <#target_generics> { let #field_name = (#arg_expr,); - let ( #(#descructuring,)* ) = self.fields; + let ( #(#destructuring,)* ) = self.fields; #builder_name { fields: ( #(#reconstructing,)* ), phantom: self.phantom, } } + #fallback_method } #[doc(hidden)] #[allow(dead_code, non_camel_case_types, non_snake_case)] @@ -571,7 +602,7 @@ impl<'a> StructInfo<'a> { )); }); - let descructuring = self.included_fields().map(|f| f.name); + let destructuring = self.included_fields().map(|f| f.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. @@ -634,7 +665,7 @@ impl<'a> StructInfo<'a> { #build_method_doc #[allow(clippy::default_trait_access, clippy::used_underscore_binding, clippy::no_effect_underscore_binding)] #build_method_visibility fn #build_method_name #build_method_generic (self) -> #output_type #build_method_where_clause { - let ( #(#descructuring,)* ) = self.fields; + let ( #(#destructuring,)* ) = self.fields; #( #assignments )* #[allow(deprecated)] From 4db6097e16fed300e85515d09ff56f21d7af2f00 Mon Sep 17 00:00:00 2001 From: "Ifiok Jr." Date: Thu, 22 Aug 2024 14:47:36 +0100 Subject: [PATCH 2/4] fix: code structure according to requested changes --- src/lib.rs | 16 ++++- tests/tests.rs | 4 +- typed-builder-macro/src/field_info.rs | 82 +++++++++----------------- typed-builder-macro/src/struct_info.rs | 10 +--- 4 files changed, 48 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c5819b50..d05414c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,19 +381,31 @@ impl Optional for (T,) { /// /// Handling multiple properties for `strip_option` /// +/// ```no_run +/// use typed_builder::TypedBuilder; +/// +/// #[derive(TypedBuilder)] +/// struct Foo { +/// #[builder(setter(strip_option(fallback = value_opt, fallback = value_opt2)))] +/// value: Option, +/// } +/// ``` +/// +/// Handling alternative properties for `strip_option` +/// /// ```compile_fail /// use typed_builder::TypedBuilder; /// /// #[derive(TypedBuilder)] /// struct Foo { -/// #[builder(setter(strip_option(fallback= "value_opt", fallback="value_opt2")))] +/// #[builder(setter(strip_option(type = value_opt, fallback = value_opt2)))] /// value: Option, /// } /// ``` /// /// Handling empty properties for `strip_option` /// -/// ```compile_fail +/// ```no_run /// use typed_builder::TypedBuilder; /// /// #[derive(TypedBuilder)] diff --git a/tests/tests.rs b/tests/tests.rs index cf4d8682..ae879d0f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -147,7 +147,7 @@ fn test_into_with_strip_option() { fn test_strip_option_with_fallback() { #[derive(PartialEq, TypedBuilder)] struct Foo { - #[builder(setter(strip_option(fallback = "x_opt")))] + #[builder(setter(strip_option(fallback = x_opt)))] x: Option, } @@ -159,7 +159,7 @@ fn test_strip_option_with_fallback() { fn test_into_with_strip_option_with_fallback() { #[derive(PartialEq, TypedBuilder)] struct Foo { - #[builder(setter(into, strip_option(fallback = "x_opt")))] + #[builder(setter(into, strip_option(fallback = x_opt)))] x: Option, } diff --git a/typed-builder-macro/src/field_info.rs b/typed-builder-macro/src/field_info.rs index f5645b57..4d18370e 100644 --- a/typed-builder-macro/src/field_info.rs +++ b/typed-builder-macro/src/field_info.rs @@ -3,9 +3,7 @@ use quote::quote_spanned; use syn::{parse::Error, spanned::Spanned}; use crate::mutator::Mutator; -use crate::util::{ - expr_to_lit_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix, ApplyMeta, AttrArg, SubAttr, -}; +use crate::util::{expr_to_lit_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix, ApplyMeta, AttrArg}; #[derive(Debug)] pub struct FieldInfo<'a> { @@ -129,7 +127,7 @@ pub struct SetterSettings { pub doc: Option, pub skip: Option, pub auto_into: Option, - pub strip_option: Option, + pub strip_option: Option, pub strip_bool: Option, pub transform: Option, pub prefix: Option, @@ -197,7 +195,7 @@ impl<'a> FieldBuilderAttr<'a> { let conflicting_transformations = [ ("transform", self.setter.transform.as_ref().map(|t| &t.span)), - ("strip_option", self.setter.strip_option.as_ref().map(|s| s.span())), + ("strip_option", self.setter.strip_option.as_ref().map(|s| &s.span)), ("strip_bool", self.setter.strip_bool.as_ref()), ]; let mut conflicting_transformations = conflicting_transformations @@ -339,21 +337,23 @@ impl ApplyMeta for SetterSettings { "into" => expr.apply_flag_to_field(&mut self.auto_into, "calling into() on the argument"), "strip_option" => { let caption = "putting the argument in Some(...)"; + match expr { AttrArg::Sub(sub) => { + let span = sub.span(); + let mut strip_option = Strip::new(span); + if self.strip_option.is_none() { - self.strip_option = Some(StripOption::from_args(sub)?); + strip_option.apply_sub_attr(sub)?; + self.strip_option = Some(strip_option); Ok(()) } else { - Err(Error::new( - sub.span(), - format!("Illegal setting - field is already {caption}"), - )) + Err(Error::new(span, format!("Illegal setting - field is already {caption}"))) } } AttrArg::Flag(flag) => { if self.strip_option.is_none() { - self.strip_option = Some(StripOption::Span(flag.span())); + self.strip_option = Some(Strip::new(flag.span())); Ok(()) } else { Err(Error::new( @@ -379,54 +379,30 @@ impl ApplyMeta for SetterSettings { } #[derive(Debug, Clone)] -pub enum StripOption { - Span(Span), - WithFallback(Span, String), +pub struct Strip { + pub fallback: Option, + span: Span, } -impl StripOption { - fn span(&self) -> &Span { - match self { - StripOption::Span(span) => span, - StripOption::WithFallback(span, _) => span, - } +impl Strip { + fn new(span: Span) -> Self { + Self { fallback: None, span } } +} - pub fn from_args(sub: SubAttr) -> Result { - let name = sub.name.clone(); - let mut total = 0; - let mut result: Result = Err(Error::new_spanned( - &name, - format!("Parameters required {:?}", name.to_string()), - )); - - for arg in sub.args::()? { - if total > 0 { - result = Err(Error::new_spanned( - arg.name(), - format!("Too many paramters {:?}", arg.name().to_string()), - )); - - continue; - } - - if arg.name().to_string().as_str() != "fallback" { - result = Err(Error::new_spanned( - arg.name(), - format!("Invalid parameter used {:?}", arg.name().to_string()), - )); - continue; +impl ApplyMeta for Strip { + fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> { + match expr.name().to_string().as_str() { + "fallback" => { + let ident: syn::Ident = expr.key_value().map(|kv| kv.parse_value())??; + self.fallback = Some(ident); + Ok(()) } - - let span = arg.span(); - let string_expr: syn::Expr = arg.key_value().map(|kv| kv.parse_value())??; - let name: String = expr_to_lit_string(&string_expr)?; - result = Ok(Self::WithFallback(span, name)); - - total += 1; + _ => Err(Error::new_spanned( + expr.name(), + format!("Invalid parameter used {:?}", expr.name().to_string()), + )), } - - result } } diff --git a/typed-builder-macro/src/struct_info.rs b/typed-builder-macro/src/struct_info.rs index 0ceea1a8..3ace99bd 100644 --- a/typed-builder-macro/src/struct_info.rs +++ b/typed-builder-macro/src/struct_info.rs @@ -3,7 +3,7 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse::Error, parse_quote, punctuated::Punctuated, GenericArgument, ItemFn, Token}; use crate::builder_attr::{IntoSetting, TypeBuilderAttr}; -use crate::field_info::{FieldInfo, StripOption}; +use crate::field_info::FieldInfo; use crate::mutator::Mutator; use crate::util::{ empty_type, empty_type_tuple, first_visibility, modify_types_generics_hack, phantom_data_for_generics, public_visibility, @@ -284,12 +284,8 @@ impl<'a> StructInfo<'a> { let body = &transform.body; (quote!(#(#params),*), quote!({ #body })) } else if let Some(ref strip_option) = field.builder_attr.setter.strip_option { - if let StripOption::WithFallback(_, fallback_name) = strip_option { - strip_option_fallback = Some(( - syn::parse_str(fallback_name)?, - quote!(#field_name: #field_type), - quote!(#arg_expr), - )); + if let Some(ref fallback) = strip_option.fallback { + strip_option_fallback = Some((fallback.clone(), quote!(#field_name: #field_type), quote!(#arg_expr))); } (quote!(#field_name: #arg_type), quote!(Some(#arg_expr))) From 6f82e91531d20fe0c840858f6fe90ec04cffd170 Mon Sep 17 00:00:00 2001 From: "Ifiok Jr." Date: Thu, 22 Aug 2024 15:20:22 +0100 Subject: [PATCH 3/4] fix: resolve further nits --- src/lib.rs | 14 +------------- typed-builder-macro/src/field_info.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d05414c2..66267cbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,7 +381,7 @@ impl Optional for (T,) { /// /// Handling multiple properties for `strip_option` /// -/// ```no_run +/// ```compile_fail /// use typed_builder::TypedBuilder; /// /// #[derive(TypedBuilder)] @@ -402,16 +402,4 @@ impl Optional for (T,) { /// value: Option, /// } /// ``` -/// -/// Handling empty properties for `strip_option` -/// -/// ```no_run -/// use typed_builder::TypedBuilder; -/// -/// #[derive(TypedBuilder)] -/// struct Foo { -/// #[builder(setter(strip_option()))] -/// value: Option, -/// } -/// ``` fn _compile_fail_tests() {} diff --git a/typed-builder-macro/src/field_info.rs b/typed-builder-macro/src/field_info.rs index 4d18370e..8d03bc10 100644 --- a/typed-builder-macro/src/field_info.rs +++ b/typed-builder-macro/src/field_info.rs @@ -341,11 +341,12 @@ impl ApplyMeta for SetterSettings { match expr { AttrArg::Sub(sub) => { let span = sub.span(); - let mut strip_option = Strip::new(span); if self.strip_option.is_none() { + let mut strip_option = Strip::new(span); strip_option.apply_sub_attr(sub)?; self.strip_option = Some(strip_option); + Ok(()) } else { Err(Error::new(span, format!("Illegal setting - field is already {caption}"))) @@ -394,6 +395,13 @@ impl ApplyMeta for Strip { fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> { match expr.name().to_string().as_str() { "fallback" => { + if self.fallback.is_some() { + return Err(Error::new_spanned( + expr.name(), + format!("Duplicate fallback parameter {:?}", expr.name().to_string()), + )); + } + let ident: syn::Ident = expr.key_value().map(|kv| kv.parse_value())??; self.fallback = Some(ident); Ok(()) From 51e1022875c04d812920f3ffbee2970dee0a0ece Mon Sep 17 00:00:00 2001 From: "Ifiok Jr." Date: Thu, 22 Aug 2024 15:33:59 +0100 Subject: [PATCH 4/4] docs: update to match ident field --- CHANGELOG.md | 2 +- src/lib.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 047b1905..80148f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added -- Add `#[builder(setter(strip_option(fallback = "field_opt")))]` to add a fallback unstripped method to the builder struct. +- Add `#[builder(setter(strip_option(fallback = field_opt)))]` to add a fallback unstripped method to the builder struct. ## 0.19.1 - 2024-07-14 ### Fixed diff --git a/src/lib.rs b/src/lib.rs index 66267cbb..647b08d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,10 +165,9 @@ use core::ops::FnOnce; /// one cannot set the field to `None` with the setter - so the only way to get it to be `None` /// is by using `#[builder(default)]` and not calling the field's setter. /// -/// - `strip_option(fallback = "field_opt")`: for `Option<...>` fields only. As above this +/// - `strip_option(fallback = field_opt)`: for `Option<...>` fields only. As above this /// still wraps the argument with `Some(...)`. The name given to the fallback method adds -/// another method to the builder without wrapping the argument in `Some`. This is useful -/// when the codebase sometimes needs to pass in optional values. You can now call with +/// another method to the builder without wrapping the argument in `Some`. You can now call /// `field_opt(Some(...))` instead of `field(...)`. /// /// - `strip_bool`: for `bool` fields only, this makes the setter receive no arguments and simply