From fe38226bf4d882e7295f926f2099bd3520739e81 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Mon, 10 Apr 2017 11:47:28 -0700 Subject: [PATCH 01/16] Added attribute and hooks in derive_builder --- derive_builder/Cargo.toml | 1 + derive_builder/src/options/field_mode.rs | 2 ++ derive_builder/src/options/field_options.rs | 2 ++ derive_builder/src/options/mod.rs | 11 +++++++++++ derive_builder/src/options/struct_mode.rs | 1 + derive_builder/tests/try_setter.rs | 18 ++++++++++++++++++ 6 files changed, 35 insertions(+) create mode 100644 derive_builder/tests/try_setter.rs diff --git a/derive_builder/Cargo.toml b/derive_builder/Cargo.toml index 962b2771..4bf760ee 100644 --- a/derive_builder/Cargo.toml +++ b/derive_builder/Cargo.toml @@ -25,6 +25,7 @@ proc-macro = true logging = [ "log", "env_logger", "derive_builder_core/logging" ] struct_default = [] skeptic_tests = ["skeptic"] +try_setter = [] [dependencies] syn = "0.11" diff --git a/derive_builder/src/options/field_mode.rs b/derive_builder/src/options/field_mode.rs index 06159ed1..da012d4c 100644 --- a/derive_builder/src/options/field_mode.rs +++ b/derive_builder/src/options/field_mode.rs @@ -81,6 +81,7 @@ impl OptionsBuilder { setter_vis: f!(setter_vis), default_expression: f!(default_expression), setter_into: f!(setter_into), + try_setter_enabled: f!(try_setter_enabled), no_std: f!(no_std), mode: mode, } @@ -133,6 +134,7 @@ impl From> for FieldOptions { field_ident: field_ident, field_type: field_type, setter_into: b.setter_into.unwrap_or(false), + try_setter_enabled: b.try_setter_enabled.unwrap_or(false), deprecation_notes: b.mode.deprecation_notes.clone(), default_expression: b.default_expression.clone(), use_default_struct: b.mode.use_default_struct, diff --git a/derive_builder/src/options/field_options.rs b/derive_builder/src/options/field_options.rs index a17d1977..0ce7f54b 100644 --- a/derive_builder/src/options/field_options.rs +++ b/derive_builder/src/options/field_options.rs @@ -31,6 +31,8 @@ pub struct FieldOptions { pub attrs: Vec, /// Bindings to libstd or libcore. pub bindings: Bindings, + /// Enables code generation for the TryInto setter. + pub try_setter_enabled: bool, } impl DefaultExpression { diff --git a/derive_builder/src/options/mod.rs b/derive_builder/src/options/mod.rs index e2b43a6c..6b8ed902 100644 --- a/derive_builder/src/options/mod.rs +++ b/derive_builder/src/options/mod.rs @@ -45,6 +45,7 @@ pub struct OptionsBuilder { setter_vis: Option, default_expression: Option, setter_into: Option, + try_setter_enabled: Option, no_std: Option, mode: Mode, } @@ -66,6 +67,7 @@ impl From for OptionsBuilder { setter_prefix: None, setter_name: None, setter_vis: None, + try_setter_enabled: None, default_expression: None, setter_into: None, no_std: None, @@ -100,6 +102,12 @@ impl OptionsBuilder where desc: "setter type conversion", map: |x: bool| { x }, } + + impl_setter!{ + ident: try_setter_enabled, + desc: "try_setter activation", + map: |x: bool| { x }, + } impl_setter!{ ident: default_expression, @@ -198,6 +206,9 @@ impl OptionsBuilder where // setter implicitly enabled self.setter_enabled(true) }, + "try_setter" => { + self.try_setter_enabled(true) + } "default" => { if !cfg!(feature = "struct_default") && self.mode.struct_mode() { let where_info = self.where_diagnostics(); diff --git a/derive_builder/src/options/struct_mode.rs b/derive_builder/src/options/struct_mode.rs index a43e7d49..0c082f97 100644 --- a/derive_builder/src/options/struct_mode.rs +++ b/derive_builder/src/options/struct_mode.rs @@ -78,6 +78,7 @@ impl From> for (StructOptions, OptionsBuilder Date: Mon, 10 Apr 2017 12:48:35 -0700 Subject: [PATCH 02/16] Happy path works. NOT YET DONE * Generate appropriate documentation for `try_` methods * Confirm core::convert::TryInto exists * Finalize declaration (`try_setter` vs. `setter(include_try)` CAVEATS * An error on a try_ with the owned builder pattern is irrecoverable because the builder was consumed and not returned * Without error_chain or the like, error handling can be verbose --- derive_builder/src/options/field_options.rs | 1 + derive_builder/tests/try_setter.rs | 64 +++++++++++++++++++-- derive_builder_core/src/bindings.rs | 9 +++ derive_builder_core/src/setter.rs | 26 ++++++++- 4 files changed, 93 insertions(+), 7 deletions(-) diff --git a/derive_builder/src/options/field_options.rs b/derive_builder/src/options/field_options.rs index 0ce7f54b..521d9788 100644 --- a/derive_builder/src/options/field_options.rs +++ b/derive_builder/src/options/field_options.rs @@ -60,6 +60,7 @@ impl FieldOptions { pub fn as_setter<'a>(&'a self) -> Setter<'a> { Setter { enabled: self.setter_enabled, + try_enabled: self.try_setter_enabled, visibility: &self.setter_visibility, pattern: self.builder_pattern, attrs: &self.attrs, diff --git a/derive_builder/tests/try_setter.rs b/derive_builder/tests/try_setter.rs index 5eb232a7..93d3a560 100644 --- a/derive_builder/tests/try_setter.rs +++ b/derive_builder/tests/try_setter.rs @@ -1,18 +1,70 @@ +#![feature(try_from)] + #[macro_use] extern crate derive_builder; mod struct_level { - use std::net::SocketAddr; + use std::convert::TryFrom; + use std::net::{IpAddr, AddrParseError}; use std::str::FromStr; - - #[derive(Debug, Builder)] + use std::string::ToString; + + #[derive(Debug, Clone, PartialEq)] + pub struct MyAddr(IpAddr); + + impl From for MyAddr { + fn from(v: IpAddr) -> Self { + MyAddr(v) + } + } + + impl<'a> TryFrom<&'a str> for MyAddr { + type Err = AddrParseError; + + fn try_from(v: &str) -> Result { + Ok(MyAddr(v.parse()?)) + } + } + + #[derive(Debug, PartialEq, Builder)] #[builder(try_setter, setter(into))] struct Lorem { - pub address: SocketAddr + pub source: MyAddr, + pub dest: MyAddr, } - + #[test] fn infallible_set() { - let _ = LoremBuilder::default().address(SocketAddr::from_str("1.2.3.4:80").unwrap()).build(); + let _ = LoremBuilder::default().source(IpAddr::from_str("1.2.3.4").unwrap()).build(); + } + + #[test] + fn fallible_set() { + let mut builder = LoremBuilder::default(); + let try_result = builder.try_source("1.2.3.4"); + let built = try_result.expect("Passed well-formed address") + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build() + .unwrap(); + assert_eq!(built, exact_helper().unwrap()); + } + + fn exact_helper() -> Result { + LoremBuilder::default() + .source(IpAddr::from_str("1.2.3.4").unwrap()) + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build() + } + + fn try_helper() -> Result { + LoremBuilder::default() + .try_source("1.2.3.4").map_err(|e| e.to_string())? + .try_dest("0.0.0.0").map_err(|e| e.to_string())? + .build() + } + + #[test] + fn with_helper() { + assert_eq!(exact_helper().unwrap(), try_helper().unwrap()); } } \ No newline at end of file diff --git a/derive_builder_core/src/bindings.rs b/derive_builder_core/src/bindings.rs index 099c1bc1..6edb3fa1 100644 --- a/derive_builder_core/src/bindings.rs +++ b/derive_builder_core/src/bindings.rs @@ -70,6 +70,15 @@ impl Bindings { ":: std :: convert :: Into" }) } + + /// TryInto trait. + pub fn try_into_trait(&self) -> RawTokens<&'static str> { + RawTokens(if self.no_std { + ":: core :: convert :: TryInto" + } else { + ":: std :: convert :: TryInto" + }) + } } #[test] diff --git a/derive_builder_core/src/setter.rs b/derive_builder_core/src/setter.rs index cfcc8908..61db6aee 100644 --- a/derive_builder_core/src/setter.rs +++ b/derive_builder_core/src/setter.rs @@ -34,6 +34,8 @@ use Bindings; pub struct Setter<'a> { /// Enables code generation for this setter fn. pub enabled: bool, + /// Enables code generation for the `try_` variant of this setter fn. + pub try_enabled: bool, /// Visibility of the setter, e.g. `syn::Visibility::Public`. pub visibility: &'a syn::Visibility, /// How the setter method takes and returns `self` (e.g. mutably). @@ -58,7 +60,7 @@ pub struct Setter<'a> { impl<'a> ToTokens for Setter<'a> { fn to_tokens(&self, tokens: &mut Tokens) { - if self.enabled { + if self.enabled || self.try_enabled { trace!("Deriving setter for `{}`.", self.field_ident); let ty = self.field_type; let pattern = self.pattern; @@ -117,6 +119,27 @@ impl<'a> ToTokens for Setter<'a> { new.#field_ident = #option::Some(#into_value); new })); + + if self.try_enabled { + let try_into = self.bindings.try_into_trait(); + let try_ty_params = quote!(>); + let try_ident = syn::Ident::new(format!("try_{}", ident)); + let result = self.bindings.result_ty(); + + tokens.append(quote!( + #(#attrs)* + #vis fn #try_ident #try_ty_params (#self_param, value: VALUE) + -> #result<#return_ty, VALUE::Err> + { + #deprecation_notes + let value : #ty = value.try_into()?; + let mut new = #self_into_return_ty; + new.#field_ident = #option::Some(value); + Ok(new) + })); + } else { + trace!("Skipping try_setter for `{}`.", self.field_ident); + } } else { trace!("Skipping setter for `{}`.", self.field_ident); } @@ -131,6 +154,7 @@ macro_rules! default_setter { () => { Setter { enabled: true, + try_enabled: false, visibility: &syn::Visibility::Public, pattern: BuilderPattern::Mutable, attrs: &vec![], From 4cec554ed1e799168cb8401d71b8a06602cefd37 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Mon, 10 Apr 2017 12:51:03 -0700 Subject: [PATCH 03/16] Removed unused feature flag --- derive_builder/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/derive_builder/Cargo.toml b/derive_builder/Cargo.toml index 4bf760ee..962b2771 100644 --- a/derive_builder/Cargo.toml +++ b/derive_builder/Cargo.toml @@ -25,7 +25,6 @@ proc-macro = true logging = [ "log", "env_logger", "derive_builder_core/logging" ] struct_default = [] skeptic_tests = ["skeptic"] -try_setter = [] [dependencies] syn = "0.11" From 85f4d5aa84e1cff67da2065cee406e94244a30ac Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Mon, 10 Apr 2017 12:57:39 -0700 Subject: [PATCH 04/16] Enabled crate-level control over try_setter activation --- derive_builder/Cargo.toml | 1 + derive_builder/src/options/field_options.rs | 2 +- derive_builder/tests/try_setter.rs | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/derive_builder/Cargo.toml b/derive_builder/Cargo.toml index 962b2771..4bf760ee 100644 --- a/derive_builder/Cargo.toml +++ b/derive_builder/Cargo.toml @@ -25,6 +25,7 @@ proc-macro = true logging = [ "log", "env_logger", "derive_builder_core/logging" ] struct_default = [] skeptic_tests = ["skeptic"] +try_setter = [] [dependencies] syn = "0.11" diff --git a/derive_builder/src/options/field_options.rs b/derive_builder/src/options/field_options.rs index 521d9788..75f8e5d4 100644 --- a/derive_builder/src/options/field_options.rs +++ b/derive_builder/src/options/field_options.rs @@ -60,7 +60,7 @@ impl FieldOptions { pub fn as_setter<'a>(&'a self) -> Setter<'a> { Setter { enabled: self.setter_enabled, - try_enabled: self.try_setter_enabled, + try_enabled: cfg!(feature = "try_setter") && self.try_setter_enabled, visibility: &self.setter_visibility, pattern: self.builder_pattern, attrs: &self.attrs, diff --git a/derive_builder/tests/try_setter.rs b/derive_builder/tests/try_setter.rs index 93d3a560..0928cf5d 100644 --- a/derive_builder/tests/try_setter.rs +++ b/derive_builder/tests/try_setter.rs @@ -1,10 +1,13 @@ -#![feature(try_from)] +#![cfg_attr(feature = "try_setter", feature(try_from))] #[macro_use] extern crate derive_builder; +#[allow(unused_imports)] mod struct_level { + #[cfg(feature = "try_setter")] use std::convert::TryFrom; + use std::net::{IpAddr, AddrParseError}; use std::str::FromStr; use std::string::ToString; @@ -18,6 +21,7 @@ mod struct_level { } } + #[cfg(feature = "try_setter")] impl<'a> TryFrom<&'a str> for MyAddr { type Err = AddrParseError; @@ -39,6 +43,7 @@ mod struct_level { } #[test] + #[cfg(feature = "try_setter")] fn fallible_set() { let mut builder = LoremBuilder::default(); let try_result = builder.try_source("1.2.3.4"); @@ -56,6 +61,7 @@ mod struct_level { .build() } + #[cfg(feature = "try_setter")] fn try_helper() -> Result { LoremBuilder::default() .try_source("1.2.3.4").map_err(|e| e.to_string())? @@ -64,6 +70,7 @@ mod struct_level { } #[test] + #[cfg(feature = "try_setter")] fn with_helper() { assert_eq!(exact_helper().unwrap(), try_helper().unwrap()); } From 0b59e93d5e76871fd24ca620bf40683dee6fbe76 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Mon, 10 Apr 2017 13:11:36 -0700 Subject: [PATCH 05/16] Fixed a dead_code warning in the try_setter test --- derive_builder/tests/try_setter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/derive_builder/tests/try_setter.rs b/derive_builder/tests/try_setter.rs index 0928cf5d..89b08fe2 100644 --- a/derive_builder/tests/try_setter.rs +++ b/derive_builder/tests/try_setter.rs @@ -54,6 +54,8 @@ mod struct_level { assert_eq!(built, exact_helper().unwrap()); } + // Allow dead code here since the test that uses this depends on the try_setter feature. + #[cfg_attr(not(feature = "try_setter"), allow(dead_code))] fn exact_helper() -> Result { LoremBuilder::default() .source(IpAddr::from_str("1.2.3.4").unwrap()) From a56e870a54ae72fd46cec7575991aa2286c147b2 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 08:29:15 -0700 Subject: [PATCH 06/16] Incorporated requested source changes * Renamed try_setter_enabled and try_enabled to `try_setter` * Removed excess #deprecation_notes * Removed `|| self.try_enabled` --- derive_builder/src/options/field_mode.rs | 4 ++-- derive_builder/src/options/field_options.rs | 4 ++-- derive_builder/src/options/mod.rs | 8 ++++---- derive_builder/src/options/struct_mode.rs | 2 +- derive_builder_core/src/setter.rs | 9 ++++----- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/derive_builder/src/options/field_mode.rs b/derive_builder/src/options/field_mode.rs index da012d4c..cea8a321 100644 --- a/derive_builder/src/options/field_mode.rs +++ b/derive_builder/src/options/field_mode.rs @@ -81,7 +81,7 @@ impl OptionsBuilder { setter_vis: f!(setter_vis), default_expression: f!(default_expression), setter_into: f!(setter_into), - try_setter_enabled: f!(try_setter_enabled), + try_setter: f!(try_setter), no_std: f!(no_std), mode: mode, } @@ -134,7 +134,7 @@ impl From> for FieldOptions { field_ident: field_ident, field_type: field_type, setter_into: b.setter_into.unwrap_or(false), - try_setter_enabled: b.try_setter_enabled.unwrap_or(false), + try_setter: b.try_setter.unwrap_or(false), deprecation_notes: b.mode.deprecation_notes.clone(), default_expression: b.default_expression.clone(), use_default_struct: b.mode.use_default_struct, diff --git a/derive_builder/src/options/field_options.rs b/derive_builder/src/options/field_options.rs index 75f8e5d4..0af51db7 100644 --- a/derive_builder/src/options/field_options.rs +++ b/derive_builder/src/options/field_options.rs @@ -32,7 +32,7 @@ pub struct FieldOptions { /// Bindings to libstd or libcore. pub bindings: Bindings, /// Enables code generation for the TryInto setter. - pub try_setter_enabled: bool, + pub try_setter: bool, } impl DefaultExpression { @@ -60,7 +60,7 @@ impl FieldOptions { pub fn as_setter<'a>(&'a self) -> Setter<'a> { Setter { enabled: self.setter_enabled, - try_enabled: cfg!(feature = "try_setter") && self.try_setter_enabled, + try_setter: self.try_setter, visibility: &self.setter_visibility, pattern: self.builder_pattern, attrs: &self.attrs, diff --git a/derive_builder/src/options/mod.rs b/derive_builder/src/options/mod.rs index 6b8ed902..477270be 100644 --- a/derive_builder/src/options/mod.rs +++ b/derive_builder/src/options/mod.rs @@ -45,7 +45,7 @@ pub struct OptionsBuilder { setter_vis: Option, default_expression: Option, setter_into: Option, - try_setter_enabled: Option, + try_setter: Option, no_std: Option, mode: Mode, } @@ -67,7 +67,7 @@ impl From for OptionsBuilder { setter_prefix: None, setter_name: None, setter_vis: None, - try_setter_enabled: None, + try_setter: None, default_expression: None, setter_into: None, no_std: None, @@ -104,7 +104,7 @@ impl OptionsBuilder where } impl_setter!{ - ident: try_setter_enabled, + ident: try_setter, desc: "try_setter activation", map: |x: bool| { x }, } @@ -207,7 +207,7 @@ impl OptionsBuilder where self.setter_enabled(true) }, "try_setter" => { - self.try_setter_enabled(true) + self.try_setter(true) } "default" => { if !cfg!(feature = "struct_default") && self.mode.struct_mode() { diff --git a/derive_builder/src/options/struct_mode.rs b/derive_builder/src/options/struct_mode.rs index 0c082f97..bf843b65 100644 --- a/derive_builder/src/options/struct_mode.rs +++ b/derive_builder/src/options/struct_mode.rs @@ -78,7 +78,7 @@ impl From> for (StructOptions, OptionsBuilder { /// Enables code generation for this setter fn. pub enabled: bool, /// Enables code generation for the `try_` variant of this setter fn. - pub try_enabled: bool, + pub try_setter: bool, /// Visibility of the setter, e.g. `syn::Visibility::Public`. pub visibility: &'a syn::Visibility, /// How the setter method takes and returns `self` (e.g. mutably). @@ -60,7 +60,7 @@ pub struct Setter<'a> { impl<'a> ToTokens for Setter<'a> { fn to_tokens(&self, tokens: &mut Tokens) { - if self.enabled || self.try_enabled { + if self.enabled { trace!("Deriving setter for `{}`.", self.field_ident); let ty = self.field_type; let pattern = self.pattern; @@ -120,7 +120,7 @@ impl<'a> ToTokens for Setter<'a> { new })); - if self.try_enabled { + if self.try_setter { let try_into = self.bindings.try_into_trait(); let try_ty_params = quote!(>); let try_ident = syn::Ident::new(format!("try_{}", ident)); @@ -131,7 +131,6 @@ impl<'a> ToTokens for Setter<'a> { #vis fn #try_ident #try_ty_params (#self_param, value: VALUE) -> #result<#return_ty, VALUE::Err> { - #deprecation_notes let value : #ty = value.try_into()?; let mut new = #self_into_return_ty; new.#field_ident = #option::Some(value); @@ -154,7 +153,7 @@ macro_rules! default_setter { () => { Setter { enabled: true, - try_enabled: false, + try_setter: false, visibility: &syn::Visibility::Public, pattern: BuilderPattern::Mutable, attrs: &vec![], From 6d497bc2ca0462890f596a3af1e2f21b5a75fc6a Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 09:47:27 -0700 Subject: [PATCH 07/16] Tests and documentation --- .travis.yml | 2 +- derive_builder/CHANGELOG.md | 10 +++ derive_builder/Cargo.toml | 1 - derive_builder/examples/try_setter.rs | 52 +++++++++++++++ derive_builder/src/lib.rs | 28 ++++++++ derive_builder_core/src/setter.rs | 66 +++++++++++++++++-- derive_builder_test/Cargo.toml | 2 +- derive_builder_test/tests/compiletests.rs | 2 +- .../tests/run-pass}/try_setter.rs | 19 +++--- 9 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 derive_builder/examples/try_setter.rs rename {derive_builder/tests => derive_builder_test/tests/run-pass}/try_setter.rs (79%) diff --git a/.travis.yml b/.travis.yml index 31926926..268fd9e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: - rust: 1.15.0 env: JOB=build CARGO_FEATURES="struct_default" - rust: nightly - env: JOB=build CARGO_FEATURES="compiletests logging" + env: JOB=build CARGO_FEATURES="nightlytests logging" - rust: nightly env: JOB=style_check allow_failures: diff --git a/derive_builder/CHANGELOG.md b/derive_builder/CHANGELOG.md index 959193bb..348fd1c4 100644 --- a/derive_builder/CHANGELOG.md +++ b/derive_builder/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.3] - 2017-04-13 + +### Added +- try_setters, e.g. `#[builder(try_setter)]`. These setters are exposed alongside the + normal field setters and allow callers to pass in values which have fallible + conversions to the needed type through `TryInto`. This attribute can be used on any + Rust channel, but will only emit setters on nightly when `#![feature(try_from)]` is + declared in the consuming crate's root; this will change when Rust issue + [#33417](https://github.com/rust-lang/rust/issues/33417) is resolved. + ## [0.4.2] - 2017-04-10 ### Fixed diff --git a/derive_builder/Cargo.toml b/derive_builder/Cargo.toml index 4bf760ee..962b2771 100644 --- a/derive_builder/Cargo.toml +++ b/derive_builder/Cargo.toml @@ -25,7 +25,6 @@ proc-macro = true logging = [ "log", "env_logger", "derive_builder_core/logging" ] struct_default = [] skeptic_tests = ["skeptic"] -try_setter = [] [dependencies] syn = "0.11" diff --git a/derive_builder/examples/try_setter.rs b/derive_builder/examples/try_setter.rs new file mode 100644 index 00000000..025c7e60 --- /dev/null +++ b/derive_builder/examples/try_setter.rs @@ -0,0 +1,52 @@ +//! This example illustrates the use of try-setters. +#![feature(try_from)] + +#[macro_use] +extern crate derive_builder; + +use std::convert::{From, TryFrom}; +use std::net::{IpAddr, AddrParseError}; +use std::str::FromStr; +use std::string::ToString; + +/// Temporary newtype hack around lack of TryFrom implementations +/// in std. The rust-lang issue on the subject says that there will be a +/// blanket impl for everything that currently implements FromStr, which +/// will make this feature much more useful for input validation. +#[derive(Debug, Clone, PartialEq)] +pub struct MyAddr(IpAddr); + +impl From for MyAddr { + fn from(v: IpAddr) -> Self { + MyAddr(v) + } +} + +impl<'a> TryFrom<&'a str> for MyAddr { + type Err = AddrParseError; + + fn try_from(v: &str) -> Result { + Ok(MyAddr(IpAddr::from_str(v)?)) + } +} + +#[derive(Builder, Debug, PartialEq)] +#[builder(try_setter, setter(into))] +struct Lorem { + pub name: String, + pub addr: MyAddr, +} + +fn main() { + create("Jane", "1.2.3.4").unwrap(); + create("Bobby", "").unwrap_err(); +} + +fn create(name: &str, addr: &str) -> Result { + // Fallible and infallible setters can be mixed freely when using + // the mutable builder pattern. + LoremBuilder::default() + .name(name) + .try_addr(addr).map_err(|e| e.to_string())? + .build() +} \ No newline at end of file diff --git a/derive_builder/src/lib.rs b/derive_builder/src/lib.rs index 854c5e3e..b14c208b 100644 --- a/derive_builder/src/lib.rs +++ b/derive_builder/src/lib.rs @@ -216,6 +216,31 @@ //! } //! ``` //! +//! ## Fallible Setters +//! +//! Alongside the normal setter methods, you can expose fallible setters which are generic over +//! the `TryInto` trait. TryInto is a not-yet-stable trait +//! (see rust-lang issue [#33417](https://github.com/rust-lang/rust/issues/33417)) similar to +//! `Into` with the key distinction that the conversion can fail, and therefore produces a `Result`. +//! +//! You can declare the `try_setter` attribute today, and your builder will work on stable Rust - +//! the fallible setters will automatically light up in future when the `try_from` feature is +//! stabilized. +//! +//! +//! ```rust,ignore +//! # #[macro_use] +//! # extern crate derive_builder; +//! # +//! #[derive(Builder, Debug, PartialEq)] +//! #[builder(try_setter, setter(into))] +//! struct Lorem { +//! pub ipsum: u8, +//! } +//! ``` +//! +//! A complete example of using fallible setters is available in `examples/try_setter.rs`. +//! //! ## Default Values //! //! You can define default values for each field via annotation by `#[builder(default="...")]`, @@ -366,6 +391,9 @@ //! - If derive_builder depends on your crate, and vice versa, then a cyclic //! dependency would occur. To break it you could try to depend on the //! [`derive_builder_core`] crate instead. +//! - The `try_setter` attribute and `owned` builder pattern are not compatible in practice; +//! an error during building will consume the builder, making it impossible to continue +//! construction. //! //! ## Debugging Info //! diff --git a/derive_builder_core/src/setter.rs b/derive_builder_core/src/setter.rs index bec0dba4..b8ef3ce8 100644 --- a/derive_builder_core/src/setter.rs +++ b/derive_builder_core/src/setter.rs @@ -131,9 +131,9 @@ impl<'a> ToTokens for Setter<'a> { #vis fn #try_ident #try_ty_params (#self_param, value: VALUE) -> #result<#return_ty, VALUE::Err> { - let value : #ty = value.try_into()?; + let converted : #ty = value.try_into()?; let mut new = #self_into_return_ty; - new.#field_ident = #option::Some(value); + new.#field_ident = #option::Some(converted); Ok(new) })); } else { @@ -244,7 +244,7 @@ mod tests { )); } - // including + // including try_setter #[test] fn full() { let attrs = vec![syn::parse_outer_attr("#[some_attr]").unwrap()]; @@ -256,14 +256,36 @@ mod tests { setter.attrs = attrs.as_slice(); setter.generic_into = true; setter.deprecation_notes = &deprecated; + setter.try_setter = true; + + // Had to hoist these out to avoid a recursion limit in the quote! + // macro invocation below. + let action = quote!( + let mut new = self; + new.foo = ::std::option::Option::Some(value.into()); + new + ); + + let try_action = quote!( + let converted : Foo = value.try_into()?; + let mut new = self; + new.foo = ::std::option::Option::Some(converted); + Ok(new) + ); + + println!("{}", quote!(#setter)); assert_eq!(quote!(#setter), quote!( #[some_attr] pub fn foo >(&mut self, value: VALUE) -> &mut Self { #deprecated - let mut new = self; - new.foo = ::std::option::Option::Some(value.into()); - new + #action + } + + #[some_attr] + pub fn try_foo>(&mut self, value: VALUE) + -> ::std::result::Result<&mut Self, VALUE::Err> { + #try_action } )); } @@ -305,4 +327,36 @@ mod tests { assert_eq!(quote!(#setter), quote!()); } + + #[test] + fn try_setter() { + let mut setter : Setter = default_setter!(); + setter.pattern = BuilderPattern::Mutable; + setter.try_setter = true; + + let setter_quote = quote!( + pub fn foo(&mut self, value: Foo) -> &mut Self { + let mut new = self; + new.foo = ::std::option::Option::Some(value); + new + } + ); + + // hoisting necessary to avoid recursion limit. + let try_setter_body = quote!( + let converted : Foo = value.try_into()?; + let mut new = self; + new.foo = ::std::option::Option::Some(converted); + Ok(new) + ); + + assert_eq!(quote!(#setter), quote!( + #setter_quote + + pub fn try_foo>(&mut self, value: VALUE) + -> ::std::result::Result<&mut Self, VALUE::Err> { + #try_setter_body + } + )); + } } diff --git a/derive_builder_test/Cargo.toml b/derive_builder_test/Cargo.toml index d93b78b1..a687f151 100644 --- a/derive_builder_test/Cargo.toml +++ b/derive_builder_test/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "src/lib.rs" [features] -compiletests = ["compiletest_rs"] +nightlytests = ["compiletest_rs"] logging = [ "derive_builder/logging" ] struct_default = [ "derive_builder/struct_default" ] diff --git a/derive_builder_test/tests/compiletests.rs b/derive_builder_test/tests/compiletests.rs index 23c07b02..022793cd 100644 --- a/derive_builder_test/tests/compiletests.rs +++ b/derive_builder_test/tests/compiletests.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "compiletests")] +#![cfg(feature = "nightlytests")] extern crate compiletest_rs as compiletest; // note: diff --git a/derive_builder/tests/try_setter.rs b/derive_builder_test/tests/run-pass/try_setter.rs similarity index 79% rename from derive_builder/tests/try_setter.rs rename to derive_builder_test/tests/run-pass/try_setter.rs index 89b08fe2..f2492b2f 100644 --- a/derive_builder/tests/try_setter.rs +++ b/derive_builder_test/tests/run-pass/try_setter.rs @@ -1,11 +1,11 @@ -#![cfg_attr(feature = "try_setter", feature(try_from))] +#![cfg_attr(feature = "nightlytests", feature(try_from))] #[macro_use] extern crate derive_builder; #[allow(unused_imports)] mod struct_level { - #[cfg(feature = "try_setter")] + #[cfg(feature = "try_from")] use std::convert::TryFrom; use std::net::{IpAddr, AddrParseError}; @@ -21,7 +21,7 @@ mod struct_level { } } - #[cfg(feature = "try_setter")] + #[cfg(feature = "nightlytests")] impl<'a> TryFrom<&'a str> for MyAddr { type Err = AddrParseError; @@ -39,11 +39,14 @@ mod struct_level { #[test] fn infallible_set() { - let _ = LoremBuilder::default().source(IpAddr::from_str("1.2.3.4").unwrap()).build(); + let _ = LoremBuilder::default() + .source(IpAddr::from_str("1.2.3.4").unwrap()) + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build(); } #[test] - #[cfg(feature = "try_setter")] + #[cfg(feature = "nightlytests")] fn fallible_set() { let mut builder = LoremBuilder::default(); let try_result = builder.try_source("1.2.3.4"); @@ -55,7 +58,7 @@ mod struct_level { } // Allow dead code here since the test that uses this depends on the try_setter feature. - #[cfg_attr(not(feature = "try_setter"), allow(dead_code))] + #[cfg_attr(not(feature = "nightlytests"), allow(dead_code))] fn exact_helper() -> Result { LoremBuilder::default() .source(IpAddr::from_str("1.2.3.4").unwrap()) @@ -63,7 +66,7 @@ mod struct_level { .build() } - #[cfg(feature = "try_setter")] + #[cfg(feature = "nightlytests")] fn try_helper() -> Result { LoremBuilder::default() .try_source("1.2.3.4").map_err(|e| e.to_string())? @@ -72,7 +75,7 @@ mod struct_level { } #[test] - #[cfg(feature = "try_setter")] + #[cfg(feature = "nightlytests")] fn with_helper() { assert_eq!(exact_helper().unwrap(), try_helper().unwrap()); } From 24d7eb4cbec8434963c34f9b4cad8c758efaf248 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 09:58:38 -0700 Subject: [PATCH 08/16] Updated integration tests --- .../tests/run-pass/try_setter.rs | 95 ++++--------------- 1 file changed, 18 insertions(+), 77 deletions(-) diff --git a/derive_builder_test/tests/run-pass/try_setter.rs b/derive_builder_test/tests/run-pass/try_setter.rs index f2492b2f..4783809c 100644 --- a/derive_builder_test/tests/run-pass/try_setter.rs +++ b/derive_builder_test/tests/run-pass/try_setter.rs @@ -1,82 +1,23 @@ #![cfg_attr(feature = "nightlytests", feature(try_from))] +use std::net::IpAddr; + #[macro_use] extern crate derive_builder; -#[allow(unused_imports)] -mod struct_level { - #[cfg(feature = "try_from")] - use std::convert::TryFrom; - - use std::net::{IpAddr, AddrParseError}; - use std::str::FromStr; - use std::string::ToString; - - #[derive(Debug, Clone, PartialEq)] - pub struct MyAddr(IpAddr); - - impl From for MyAddr { - fn from(v: IpAddr) -> Self { - MyAddr(v) - } - } - - #[cfg(feature = "nightlytests")] - impl<'a> TryFrom<&'a str> for MyAddr { - type Err = AddrParseError; - - fn try_from(v: &str) -> Result { - Ok(MyAddr(v.parse()?)) - } - } - - #[derive(Debug, PartialEq, Builder)] - #[builder(try_setter, setter(into))] - struct Lorem { - pub source: MyAddr, - pub dest: MyAddr, - } - - #[test] - fn infallible_set() { - let _ = LoremBuilder::default() - .source(IpAddr::from_str("1.2.3.4").unwrap()) - .dest(IpAddr::from_str("0.0.0.0").unwrap()) - .build(); - } - - #[test] - #[cfg(feature = "nightlytests")] - fn fallible_set() { - let mut builder = LoremBuilder::default(); - let try_result = builder.try_source("1.2.3.4"); - let built = try_result.expect("Passed well-formed address") - .dest(IpAddr::from_str("0.0.0.0").unwrap()) - .build() - .unwrap(); - assert_eq!(built, exact_helper().unwrap()); - } - - // Allow dead code here since the test that uses this depends on the try_setter feature. - #[cfg_attr(not(feature = "nightlytests"), allow(dead_code))] - fn exact_helper() -> Result { - LoremBuilder::default() - .source(IpAddr::from_str("1.2.3.4").unwrap()) - .dest(IpAddr::from_str("0.0.0.0").unwrap()) - .build() - } - - #[cfg(feature = "nightlytests")] - fn try_helper() -> Result { - LoremBuilder::default() - .try_source("1.2.3.4").map_err(|e| e.to_string())? - .try_dest("0.0.0.0").map_err(|e| e.to_string())? - .build() - } - - #[test] - #[cfg(feature = "nightlytests")] - fn with_helper() { - assert_eq!(exact_helper().unwrap(), try_helper().unwrap()); - } -} \ No newline at end of file +#[derive(Debug, Clone, Builder)] +#[builder(try_setter)] +pub struct Lorem { + source: IpAddr, + dest: IpAddr, + name: String, +} + +#[derive(Default, Debug, Clone, Builder)] +#[builder(default, setter(prefix = "set", into), try_setter)] +pub struct Ipsum { + source: Option, + name: String, +} + +fn main() { } From 79491a563d3acb42a76f6056b34b83afb003da40 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 10:40:52 -0700 Subject: [PATCH 09/16] Fixed recursion limit for tests in derive_builder_core --- derive_builder_core/src/lib.rs | 1 + derive_builder_core/src/setter.rs | 45 +++++++++---------------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/derive_builder_core/src/lib.rs b/derive_builder_core/src/lib.rs index e97873e0..9518ada5 100644 --- a/derive_builder_core/src/lib.rs +++ b/derive_builder_core/src/lib.rs @@ -22,6 +22,7 @@ //! [`derive_builder_core`]: https://!crates.io/crates/derive_builder_core #![deny(warnings, missing_docs)] +#![cfg_attr(test, recursion_limit = "100")] extern crate proc_macro; extern crate syn; diff --git a/derive_builder_core/src/setter.rs b/derive_builder_core/src/setter.rs index b8ef3ce8..0d6ea26d 100644 --- a/derive_builder_core/src/setter.rs +++ b/derive_builder_core/src/setter.rs @@ -258,34 +258,22 @@ mod tests { setter.deprecation_notes = &deprecated; setter.try_setter = true; - // Had to hoist these out to avoid a recursion limit in the quote! - // macro invocation below. - let action = quote!( - let mut new = self; - new.foo = ::std::option::Option::Some(value.into()); - new - ); - - let try_action = quote!( - let converted : Foo = value.try_into()?; - let mut new = self; - new.foo = ::std::option::Option::Some(converted); - Ok(new) - ); - - println!("{}", quote!(#setter)); - assert_eq!(quote!(#setter), quote!( #[some_attr] pub fn foo >(&mut self, value: VALUE) -> &mut Self { #deprecated - #action + let mut new = self; + new.foo = ::std::option::Option::Some(value.into()); + new } #[some_attr] pub fn try_foo>(&mut self, value: VALUE) -> ::std::result::Result<&mut Self, VALUE::Err> { - #try_action + let converted : Foo = value.try_into()?; + let mut new = self; + new.foo = ::std::option::Option::Some(converted); + Ok(new) } )); } @@ -334,28 +322,19 @@ mod tests { setter.pattern = BuilderPattern::Mutable; setter.try_setter = true; - let setter_quote = quote!( + assert_eq!(quote!(#setter), quote!( pub fn foo(&mut self, value: Foo) -> &mut Self { let mut new = self; new.foo = ::std::option::Option::Some(value); new } - ); - - // hoisting necessary to avoid recursion limit. - let try_setter_body = quote!( - let converted : Foo = value.try_into()?; - let mut new = self; - new.foo = ::std::option::Option::Some(converted); - Ok(new) - ); - - assert_eq!(quote!(#setter), quote!( - #setter_quote pub fn try_foo>(&mut self, value: VALUE) -> ::std::result::Result<&mut Self, VALUE::Err> { - #try_setter_body + let converted : Foo = value.try_into()?; + let mut new = self; + new.foo = ::std::option::Option::Some(converted); + Ok(new) } )); } From fd7deeb0b5b6a4b97b667f88cd1e1d259f0ac028 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 11:10:50 -0700 Subject: [PATCH 10/16] Updated docs and changelog to reflect lack of lightup --- derive_builder/CHANGELOG.md | 8 ++++---- derive_builder/examples/try_setter.rs | 4 +++- derive_builder/src/lib.rs | 27 ++++++++++++++++++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/derive_builder/CHANGELOG.md b/derive_builder/CHANGELOG.md index 348fd1c4..c5bb3a99 100644 --- a/derive_builder/CHANGELOG.md +++ b/derive_builder/CHANGELOG.md @@ -6,10 +6,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - try_setters, e.g. `#[builder(try_setter)]`. These setters are exposed alongside the - normal field setters and allow callers to pass in values which have fallible - conversions to the needed type through `TryInto`. This attribute can be used on any - Rust channel, but will only emit setters on nightly when `#![feature(try_from)]` is - declared in the consuming crate's root; this will change when Rust issue + normal field setters and allow callers to pass in values which have + fallible conversions to the needed type through `TryInto`. This attribute + can only be used on nightly when until `#![feature(try_from)]` is declared + in the consuming crate's root; this will change when Rust issue [#33417](https://github.com/rust-lang/rust/issues/33417) is resolved. ## [0.4.2] - 2017-04-10 diff --git a/derive_builder/examples/try_setter.rs b/derive_builder/examples/try_setter.rs index 025c7e60..6352ef32 100644 --- a/derive_builder/examples/try_setter.rs +++ b/derive_builder/examples/try_setter.rs @@ -1,10 +1,12 @@ //! This example illustrates the use of try-setters. +//! Tests are suppressed so that this doesn't break the build on stable. +#![cfg(not(test))] #![feature(try_from)] #[macro_use] extern crate derive_builder; -use std::convert::{From, TryFrom}; +use std::convert::TryFrom; use std::net::{IpAddr, AddrParseError}; use std::str::FromStr; use std::string::ToString; diff --git a/derive_builder/src/lib.rs b/derive_builder/src/lib.rs index b14c208b..60bdf183 100644 --- a/derive_builder/src/lib.rs +++ b/derive_builder/src/lib.rs @@ -223,20 +223,41 @@ //! (see rust-lang issue [#33417](https://github.com/rust-lang/rust/issues/33417)) similar to //! `Into` with the key distinction that the conversion can fail, and therefore produces a `Result`. //! -//! You can declare the `try_setter` attribute today, and your builder will work on stable Rust - -//! the fallible setters will automatically light up in future when the `try_from` feature is -//! stabilized. +//! You can only declare the `try_setter` attribute today if you're targeting nightly, and you have +//! to add `#![feature(try_from)]` to your crate to use it. //! //! //! ```rust,ignore +//! #![feature(try_from)] //! # #[macro_use] //! # extern crate derive_builder; //! # //! #[derive(Builder, Debug, PartialEq)] //! #[builder(try_setter, setter(into))] //! struct Lorem { +//! pub name: String, //! pub ipsum: u8, //! } +//! +//! #[derive(Builder, Debug, PartialEq)] +//! struct Ipsum { +//! #[builder(try_setter, setter(into, name = "foo"))] +//! pub dolor: u8, +//! } +//! +//! fn main() { +//! LoremBuilder::default() +//! .try_ipsum(1u16).unwrap() +//! .name("hello") +//! .build() +//! .expect("1 fits into a u8"); +//! +//! IpsumBuilder::default() +//! .try_foo(1u16) +//! .unwrap() +//! .build() +//! .expect("1 fits into a u8"); +//! } //! ``` //! //! A complete example of using fallible setters is available in `examples/try_setter.rs`. From 79e447430fe53d600c2937c3d95dd257baf0ca96 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 11:12:16 -0700 Subject: [PATCH 11/16] Moved try_setter integration test out of compiletests --- derive_builder_test/tests/{run-pass => }/try_setter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename derive_builder_test/tests/{run-pass => }/try_setter.rs (96%) diff --git a/derive_builder_test/tests/run-pass/try_setter.rs b/derive_builder_test/tests/try_setter.rs similarity index 96% rename from derive_builder_test/tests/run-pass/try_setter.rs rename to derive_builder_test/tests/try_setter.rs index 4783809c..c5a238ae 100644 --- a/derive_builder_test/tests/run-pass/try_setter.rs +++ b/derive_builder_test/tests/try_setter.rs @@ -20,4 +20,4 @@ pub struct Ipsum { name: String, } -fn main() { } +fn main() { } \ No newline at end of file From 0600c052ff0b79b0ed7ff713d44727a0af94131d Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 11:47:32 -0700 Subject: [PATCH 12/16] Trying to fix tests again by blocking the try_setter example from running --- derive_builder/examples/try_setter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/derive_builder/examples/try_setter.rs b/derive_builder/examples/try_setter.rs index 6352ef32..76ccc326 100644 --- a/derive_builder/examples/try_setter.rs +++ b/derive_builder/examples/try_setter.rs @@ -1,6 +1,6 @@ //! This example illustrates the use of try-setters. -//! Tests are suppressed so that this doesn't break the build on stable. -#![cfg(not(test))] +//! Tests are suppressed using a fake feature so that this doesn't break the build on stable. +#![cfg(feature = "try_from")] #![feature(try_from)] #[macro_use] From 15104198b6b67f057143440ad94c9aa1a9319a13 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 12:57:25 -0700 Subject: [PATCH 13/16] Revived integration tests --- derive_builder_test/tests/try_setter.rs | 111 ++++++++++++++++++++---- 1 file changed, 93 insertions(+), 18 deletions(-) diff --git a/derive_builder_test/tests/try_setter.rs b/derive_builder_test/tests/try_setter.rs index c5a238ae..b0d9d0a8 100644 --- a/derive_builder_test/tests/try_setter.rs +++ b/derive_builder_test/tests/try_setter.rs @@ -1,23 +1,98 @@ #![cfg_attr(feature = "nightlytests", feature(try_from))] -use std::net::IpAddr; - #[macro_use] extern crate derive_builder; -#[derive(Debug, Clone, Builder)] -#[builder(try_setter)] -pub struct Lorem { - source: IpAddr, - dest: IpAddr, - name: String, -} - -#[derive(Default, Debug, Clone, Builder)] -#[builder(default, setter(prefix = "set", into), try_setter)] -pub struct Ipsum { - source: Option, - name: String, -} - -fn main() { } \ No newline at end of file +#[allow(unused_imports)] +mod struct_level { + #[cfg(feature = "nightlytests")] + use std::convert::TryFrom; + + use std::net::{IpAddr, AddrParseError}; + use std::str::FromStr; + use std::string::ToString; + + #[derive(Debug, Clone, PartialEq)] + pub struct MyAddr(IpAddr); + + impl From for MyAddr { + fn from(v: IpAddr) -> Self { + MyAddr(v) + } + } + + #[cfg(feature = "nightlytests")] + impl<'a> TryFrom<&'a str> for MyAddr { + type Err = AddrParseError; + + fn try_from(v: &str) -> Result { + Ok(MyAddr(v.parse()?)) + } + } + + #[derive(Debug, PartialEq, Builder)] + #[builder(try_setter, setter(into))] + struct Lorem { + pub source: MyAddr, + pub dest: MyAddr, + } + + #[derive(Debug, PartialEq, Builder)] + #[builder(try_setter, setter(into, prefix = "set"))] + struct Ipsum { + pub source: MyAddr + } + + // Allow dead code here since the test that uses this depends on the try_setter feature. + #[cfg_attr(not(feature = "nightlytests"), allow(dead_code))] + fn exact_helper() -> Result { + LoremBuilder::default() + .source(IpAddr::from_str("1.2.3.4").unwrap()) + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build() + } + + #[cfg(feature = "nightlytests")] + fn try_helper() -> Result { + LoremBuilder::default() + .try_source("1.2.3.4").map_err(|e| e.to_string())? + .try_dest("0.0.0.0").map_err(|e| e.to_string())? + .build() + } + + #[test] + fn infallible_set() { + let _ = LoremBuilder::default() + .source(IpAddr::from_str("1.2.3.4").unwrap()) + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build(); + } + + #[test] + #[cfg(feature = "nightlytests")] + fn fallible_set() { + let mut builder = LoremBuilder::default(); + let try_result = builder.try_source("1.2.3.4"); + let built = try_result.expect("Passed well-formed address") + .dest(IpAddr::from_str("0.0.0.0").unwrap()) + .build() + .unwrap(); + assert_eq!(built, exact_helper().unwrap()); + } + + #[test] + #[cfg(feature = "nightlytests")] + fn with_helper() { + assert_eq!(exact_helper().unwrap(), try_helper().unwrap()); + } + + #[test] + #[cfg(feature = "nightlytests")] + fn renamed() { + IpsumBuilder::default() + .try_set_source("0.0.0.0") + .unwrap() + .build() + .expect("All fields were provided"); + } +} \ No newline at end of file From ec3cc06c6b6fcc0765545500647c53b54be91fa3 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 15:58:24 -0700 Subject: [PATCH 14/16] Removing the try_setter example outright. That example seems to be causing many/all of the failures. --- derive_builder/examples/try_setter.rs | 54 --------------------------- derive_builder/src/lib.rs | 2 - 2 files changed, 56 deletions(-) delete mode 100644 derive_builder/examples/try_setter.rs diff --git a/derive_builder/examples/try_setter.rs b/derive_builder/examples/try_setter.rs deleted file mode 100644 index 76ccc326..00000000 --- a/derive_builder/examples/try_setter.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! This example illustrates the use of try-setters. -//! Tests are suppressed using a fake feature so that this doesn't break the build on stable. -#![cfg(feature = "try_from")] -#![feature(try_from)] - -#[macro_use] -extern crate derive_builder; - -use std::convert::TryFrom; -use std::net::{IpAddr, AddrParseError}; -use std::str::FromStr; -use std::string::ToString; - -/// Temporary newtype hack around lack of TryFrom implementations -/// in std. The rust-lang issue on the subject says that there will be a -/// blanket impl for everything that currently implements FromStr, which -/// will make this feature much more useful for input validation. -#[derive(Debug, Clone, PartialEq)] -pub struct MyAddr(IpAddr); - -impl From for MyAddr { - fn from(v: IpAddr) -> Self { - MyAddr(v) - } -} - -impl<'a> TryFrom<&'a str> for MyAddr { - type Err = AddrParseError; - - fn try_from(v: &str) -> Result { - Ok(MyAddr(IpAddr::from_str(v)?)) - } -} - -#[derive(Builder, Debug, PartialEq)] -#[builder(try_setter, setter(into))] -struct Lorem { - pub name: String, - pub addr: MyAddr, -} - -fn main() { - create("Jane", "1.2.3.4").unwrap(); - create("Bobby", "").unwrap_err(); -} - -fn create(name: &str, addr: &str) -> Result { - // Fallible and infallible setters can be mixed freely when using - // the mutable builder pattern. - LoremBuilder::default() - .name(name) - .try_addr(addr).map_err(|e| e.to_string())? - .build() -} \ No newline at end of file diff --git a/derive_builder/src/lib.rs b/derive_builder/src/lib.rs index 60bdf183..3a6e85c2 100644 --- a/derive_builder/src/lib.rs +++ b/derive_builder/src/lib.rs @@ -260,8 +260,6 @@ //! } //! ``` //! -//! A complete example of using fallible setters is available in `examples/try_setter.rs`. -//! //! ## Default Values //! //! You can define default values for each field via annotation by `#[builder(default="...")]`, From 12daa64cc159678659b3d2ae4bba48bdcce2a0a4 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 16:31:29 -0700 Subject: [PATCH 15/16] Made the entire try_setter test dependent on nightly. --- derive_builder_test/tests/try_setter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/derive_builder_test/tests/try_setter.rs b/derive_builder_test/tests/try_setter.rs index b0d9d0a8..acdf5e17 100644 --- a/derive_builder_test/tests/try_setter.rs +++ b/derive_builder_test/tests/try_setter.rs @@ -3,6 +3,7 @@ #[macro_use] extern crate derive_builder; +#[cfg(feature = "nightlytests")] #[allow(unused_imports)] mod struct_level { #[cfg(feature = "nightlytests")] @@ -43,8 +44,6 @@ mod struct_level { pub source: MyAddr } - // Allow dead code here since the test that uses this depends on the try_setter feature. - #[cfg_attr(not(feature = "nightlytests"), allow(dead_code))] fn exact_helper() -> Result { LoremBuilder::default() .source(IpAddr::from_str("1.2.3.4").unwrap()) From c629cbd1b1ed8662dac9d21ab960d9999fc2f1e0 Mon Sep 17 00:00:00 2001 From: Ted Driggs Date: Tue, 11 Apr 2017 16:46:05 -0700 Subject: [PATCH 16/16] TryFrom's associated type was changed from Err to Error https://github.com/rust-lang/rust/pull/40281 --- derive_builder_core/src/setter.rs | 6 +++--- derive_builder_test/tests/try_setter.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/derive_builder_core/src/setter.rs b/derive_builder_core/src/setter.rs index 0d6ea26d..1841c1c7 100644 --- a/derive_builder_core/src/setter.rs +++ b/derive_builder_core/src/setter.rs @@ -129,7 +129,7 @@ impl<'a> ToTokens for Setter<'a> { tokens.append(quote!( #(#attrs)* #vis fn #try_ident #try_ty_params (#self_param, value: VALUE) - -> #result<#return_ty, VALUE::Err> + -> #result<#return_ty, VALUE::Error> { let converted : #ty = value.try_into()?; let mut new = #self_into_return_ty; @@ -269,7 +269,7 @@ mod tests { #[some_attr] pub fn try_foo>(&mut self, value: VALUE) - -> ::std::result::Result<&mut Self, VALUE::Err> { + -> ::std::result::Result<&mut Self, VALUE::Error> { let converted : Foo = value.try_into()?; let mut new = self; new.foo = ::std::option::Option::Some(converted); @@ -330,7 +330,7 @@ mod tests { } pub fn try_foo>(&mut self, value: VALUE) - -> ::std::result::Result<&mut Self, VALUE::Err> { + -> ::std::result::Result<&mut Self, VALUE::Error> { let converted : Foo = value.try_into()?; let mut new = self; new.foo = ::std::option::Option::Some(converted); diff --git a/derive_builder_test/tests/try_setter.rs b/derive_builder_test/tests/try_setter.rs index acdf5e17..49d28086 100644 --- a/derive_builder_test/tests/try_setter.rs +++ b/derive_builder_test/tests/try_setter.rs @@ -24,9 +24,9 @@ mod struct_level { #[cfg(feature = "nightlytests")] impl<'a> TryFrom<&'a str> for MyAddr { - type Err = AddrParseError; + type Error = AddrParseError; - fn try_from(v: &str) -> Result { + fn try_from(v: &str) -> Result { Ok(MyAddr(v.parse()?)) } }