-
Notifications
You must be signed in to change notification settings - Fork 96
Closed
Description
Here is a loose idea. It is mostly meant as an inspiration..
It could be to have a builder that wraps a Result. This would allow the builder to validate fields during the build process, and keep track of errors. Rust already allows you to do this with the try!(), macro, and the ?-operator has been accepted as RFC 243, but I still think that it could be nice to have a builder that keeps track of errors.
Here is what the code could look like
// The following only works on unstable
![feature(try_from)]
use std::convert::{TryFrom, TryInto};
#[derive(Debug)]
enum MyError {
TooOld
}
// I am not fully sure if it makes sense to annotate the fields
// #[builder(PersonBuilder, MyError)]
#[derive(Default)]
struct Person {
name: String,
// #[try_from]
age: Age,
// #[try_from]
mail: Mail
}
#[derive(Default)]
struct Age(u32);
impl TryFrom<u32> for Age {
type Err = MyError;
fn try_from(age: u32) -> Result<Age,MyError> {
if age > 200 {
Err(MyError::TooOld)
} else {
Ok(Age(age))
}
}
}
#[derive(Default)]
struct Mail(String);
// We should probably have TryFrom<&str> here
impl TryFrom<String> for Mail {
type Err = MyError;
fn try_from(mail: String) -> Result<Mail,MyError>{
// Parsing goes here
Ok(Mail(mail.into()))
}
}
fn main() {
let _person: Result<Person, MyError> = PersonBuilder::default()
.name("bob".to_string())
.age(25)
.mail("[email protected]".to_string())
.build();
}
Here is the code that could be derived behind the scenes
struct PersonBuilder {
inner: Result<Person, MyError>
}
impl PersonBuilder {
fn name(mut self, name: String) -> PersonBuilder {
if let &mut Ok(ref mut inner) = &mut self.inner {
inner.name = name
};
self
}
// This code can probably be prettyfied
fn age<T: TryInto<Age, Err=MyError>>(mut self, age: T) -> PersonBuilder {
match age.try_into() {
Err(e) => if self.inner.is_ok() {
self.inner = Err(e)
},
Ok(v) => if let &mut Ok(ref mut inner) = &mut self.inner {
inner.age = v
}
};
self
}
fn mail<T: TryInto<Mail, Err=MyError>>(mut self, mail: T) -> PersonBuilder {
match mail.try_into() {
Err(e) => if self.inner.is_ok() {
self.inner = Err(e)
},
Ok(v) => if let &mut Ok(ref mut inner) = &mut self.inner {
inner.mail = v
}
};
self
}
fn build(self) -> Result<Person,MyError> {
self.inner
}
}
impl Default for PersonBuilder {
fn default() -> PersonBuilder {
PersonBuilder{
inner: Ok(Person::default())
}
}
}
colin-kiegel, compressed and LooMaclin