Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only generate ValidationError when needed #203

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions derive_builder/CHANGELOG.md
Expand Up @@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased
- Only generate a validation error variant when necessary #201

## [0.10.0] - 2021-03-31
- Requires Rust 1.40.0 or newer (was 1.37.0) #169
- Logging feature is removed #177
Expand Down
16 changes: 14 additions & 2 deletions derive_builder/examples/custom_defaults.rs
@@ -1,7 +1,19 @@
use derive_builder::UninitializedFieldError;

#[macro_use]
extern crate derive_builder;

/// Marker function that tells `derive_builder` to emit a `ValidationError` variant.
///
/// Without this, `derive_builder` would believe that only uninitialized field errors
/// are possible in the `build()` method, and there would be a cryptic error message
/// about a missing conversion from `String`.
fn phantom_validate<T>(_: T) -> Result<(), String> {
Ok(())
}

#[derive(Builder, PartialEq, Debug)]
#[builder(build_fn(validate = "phantom_validate"))]
struct Lorem {
ipsum: String,
#[builder(default = "self.default_dolor()?")]
Expand All @@ -13,10 +25,10 @@ struct Lorem {
}

impl LoremBuilder {
fn default_dolor(&self) -> Result<String, String> {
fn default_dolor(&self) -> Result<String, UninitializedFieldError> {
self.ipsum
.clone()
.ok_or_else(|| "ipsum must be initialized to build dolor".to_string())
.ok_or_else(|| UninitializedFieldError::new("ipsum"))
}

fn default_sit(&self) -> Result<String, String> {
Expand Down
10 changes: 7 additions & 3 deletions derive_builder/src/lib.rs
Expand Up @@ -342,22 +342,26 @@
//! ```rust
//! # #[macro_use]
//! # extern crate derive_builder;
//! # use derive_builder::UninitializedFieldError;
//! #
//! # #[derive(Builder, PartialEq, Debug)]
//! struct Lorem {
//! ipsum: String,
//! // Custom defaults can delegate to helper methods
//! // and pass errors to the enclosing `build()` method via `?`.
//! //
//! // When doing this, it is recommended that you use a custom error type
//! // unless you are only returning UninitializedFieldError.
//! #[builder(default = "self.default_dolor()?")]
//! dolor: String,
//! }
//!
//! impl LoremBuilder {
//! // Private helper method with access to the builder struct.
//! fn default_dolor(&self) -> Result<String, String> {
//! fn default_dolor(&self) -> Result<String, UninitializedFieldError> {
//! match self.ipsum {
//! Some(ref x) if x.chars().count() > 3 => Ok(format!("dolor {}", x)),
//! _ => Err("ipsum must at least 3 chars to build dolor".to_string()),
//! Some(ref x) => Ok(format!("dolor {}", x)),
//! _ => Err(UninitializedFieldError::new("ipsum")),
//! }
//! }
//! }
Expand Down
30 changes: 26 additions & 4 deletions derive_builder/tests/try_setter.rs
Expand Up @@ -4,7 +4,8 @@ extern crate derive_builder;
use std::convert::TryFrom;
use std::net::{AddrParseError, IpAddr};
use std::str::FromStr;
use std::string::ToString;

use derive_builder::UninitializedFieldError;

#[derive(Debug, Clone, PartialEq)]
pub struct MyAddr(IpAddr);
Expand All @@ -23,8 +24,29 @@ impl<'a> TryFrom<&'a str> for MyAddr {
}
}

#[derive(Debug)]
enum LoremBuilderError {
UninitializedField(&'static str),
InvalidValue { field: &'static str, error: String },
}

impl LoremBuilderError {
fn invalid_value(field: &'static str, error: impl std::fmt::Display) -> Self {
Self::InvalidValue {
field,
error: format!("{}", error),
}
}
}

impl From<UninitializedFieldError> for LoremBuilderError {
fn from(e: UninitializedFieldError) -> Self {
Self::UninitializedField(e.field_name())
}
}

#[derive(Debug, PartialEq, Builder)]
#[builder(try_setter, setter(into))]
#[builder(try_setter, setter(into), build_fn(error = "LoremBuilderError"))]
struct Lorem {
pub source: MyAddr,
pub dest: MyAddr,
Expand All @@ -46,9 +68,9 @@ fn exact_helper() -> Result<Lorem, LoremBuilderError> {
fn try_helper() -> Result<Lorem, LoremBuilderError> {
LoremBuilder::default()
.try_source("1.2.3.4")
.map_err(|e| e.to_string())?
.map_err(|e| LoremBuilderError::invalid_value("source", e))?
.try_dest("0.0.0.0")
.map_err(|e| e.to_string())?
.map_err(|e| LoremBuilderError::invalid_value("dest", e))?
.build()
}

Expand Down
2 changes: 1 addition & 1 deletion derive_builder_core/src/block.rs
Expand Up @@ -67,7 +67,7 @@ mod test {
use super::*;

#[test]
#[should_panic(expected = r#"LexError"#)]
#[should_panic]
fn block_invalid_token_trees() {
Block::from_str("let x = 2; { x+1").unwrap();
}
Expand Down