-
Notifications
You must be signed in to change notification settings - Fork 52
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
Support deriving builder for enums #129
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -232,6 +232,44 @@ use core::ops::FnOnce; | |
/// Struct::builder().x(2).x_into_b().x_into_b().x_into_b_field().inc_a(2).build(), | ||
/// Struct {x: 2, a: 3, b: vec![2, 2, 2]}); | ||
/// ``` | ||
/// | ||
/// # Enum | ||
/// `TypedBuilder` also supports deriving builder of enums. | ||
/// By default (inconfigurable though), `TypedBuilder` generates builder methods with | ||
/// the name of "snake_case" of variant names. | ||
/// For example, the builder of `Foo::BarQux` is created by `Foo::bar_qux()`. | ||
/// Note that it does not support enums with generics or lifetime. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? I mean, obviously you'd have to specify (if they cannot be inferred) the generics and lifetimes that are only relevant for the variants not chosen, but that is no different from regular Rust enums. |
||
/// | ||
/// You can specify `#[builder(...)]` on enums, on variants, and on any fields. | ||
/// But since `TypedBuilder` internally generates a builder struct each enum variant, | ||
/// some subsections are prohibited; you cannot specify `builder_method(name=...)`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this could be better worded? From the examples I understand that |
||
/// `builder_type(name=...)`, `build_method(into=...)` on enums. | ||
/// ``` | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(PartialEq, TypedBuilder)] | ||
/// #[builder(field_defaults(setter(into)))] | ||
/// enum Foo { | ||
/// Bar { | ||
/// x: i32, | ||
/// }, | ||
/// // builder method is `.baz_qux()`, "snake_case" of variant names | ||
/// #[builder(field_defaults(setter(prefix = "with_")))] | ||
/// BazQux { | ||
/// #[builder(default, setter(strip_option))] | ||
/// y: Option<i32>, | ||
/// z: String, | ||
/// }, | ||
/// } | ||
/// | ||
/// assert!( | ||
/// Foo::bar().x(1).build() | ||
/// == Foo::Bar { x: 1 }); | ||
/// | ||
/// assert!( | ||
/// Foo::baz_qux().with_y(1).with_z("bar").build() | ||
/// == Foo::BazQux { y: Some(1), z: "bar".to_owned() }); | ||
/// ``` | ||
pub use typed_builder_macro::TypedBuilder; | ||
|
||
#[doc(hidden)] | ||
|
@@ -347,4 +385,114 @@ impl<T> Optional<T> for (T,) { | |
/// #[deny(deprecated)] | ||
/// Foo::builder().value(42).build(); | ||
///``` | ||
fn _compile_fail_tests() {} | ||
fn _struct_compile_fail_tests() {} | ||
|
||
#[doc(hidden)] | ||
/// Enum with generics: | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo<T> { | ||
/// Bar { value: T }, | ||
/// } | ||
/// ``` | ||
/// | ||
/// Enum with lifetime: | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo<'a> { | ||
/// Bar { value: &'a i32 }, | ||
/// } | ||
/// ``` | ||
/// | ||
/// Enum with builder_method(...): | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// #[builder(builder_method(name=builder))] | ||
/// enum Foo { | ||
/// Bar { value: i32 }, | ||
/// } | ||
/// ``` | ||
/// | ||
/// But for variant: | ||
/// | ||
/// ``` | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo { | ||
/// #[builder(builder_method(name=builder))] | ||
/// Bar { value: i32 }, | ||
/// } | ||
/// | ||
/// let _ = Foo::builder().value(0).build(); | ||
/// ``` | ||
/// | ||
/// Enum with builder_type(...): | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// #[builder(builder_type(name=FooBuilder))] | ||
/// enum Foo { | ||
/// Bar { value: i32 }, | ||
/// } | ||
/// ``` | ||
/// | ||
/// But for variant: | ||
/// | ||
/// ``` | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo { | ||
/// #[builder(builder_type(name=CustomBuilder))] | ||
/// Bar { value: i32 }, | ||
/// } | ||
/// | ||
/// let _: CustomBuilder = Foo::bar(); | ||
/// ``` | ||
/// | ||
/// Enum with build_method(into): | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// #[builder(build_method(into))] | ||
/// enum Foo { | ||
/// Bar { value: i32 }, | ||
/// } | ||
/// ``` | ||
/// | ||
/// Enum with tuple variants: | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo { | ||
/// Bar(i32), | ||
/// } | ||
/// ``` | ||
/// | ||
/// Enum with unit variants: | ||
/// | ||
/// ```compile_fail | ||
/// use typed_builder::TypedBuilder; | ||
/// | ||
/// #[derive(TypedBuilder)] | ||
/// enum Foo { | ||
/// Bar, | ||
/// } | ||
/// ``` | ||
fn _enum_compile_fail_tests() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
use typed_builder::TypedBuilder; | ||
|
||
#[test] | ||
fn test_simple() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum Foo { | ||
Bar { x: i32 }, | ||
Baz { y: i32, z: String }, | ||
} | ||
|
||
assert_eq!(Foo::bar().x(1).build(), Foo::Bar { x: 1 }); | ||
assert_eq!( | ||
Foo::baz().y(2).z("z".to_owned()).build(), | ||
Foo::Baz { y: 2, z: "z".to_owned() } | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_into() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
#[builder(field_defaults(setter(into)))] | ||
enum Foo { | ||
Bar { | ||
x: i32, | ||
}, | ||
Baz { | ||
#[builder(setter(!into))] | ||
y: u32, | ||
z: String, | ||
}, | ||
} | ||
|
||
assert_eq!(Foo::bar().x(1_u8).build(), Foo::Bar { x: 1 }); | ||
assert_eq!(Foo::baz().y(2).z("z").build(), Foo::Baz { y: 2, z: "z".to_owned() }); | ||
} | ||
|
||
#[test] | ||
fn test_default() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum Foo { | ||
#[builder(field_defaults(default = 1))] | ||
Bar { | ||
#[builder(default = 2)] | ||
x: i32, | ||
y: i32, | ||
}, | ||
Baz { | ||
#[builder(default = Some(3), setter(strip_option))] | ||
y: Option<i32>, | ||
#[builder(default = vec![1,2,3], setter(into))] | ||
z: Vec<i32>, | ||
}, | ||
} | ||
|
||
assert_eq!(Foo::bar().build(), Foo::Bar { x: 2, y: 1 }); | ||
assert_eq!(Foo::bar().x(3).y(4).build(), Foo::Bar { x: 3, y: 4 }); | ||
assert_eq!( | ||
Foo::baz().build(), | ||
Foo::Baz { | ||
y: Some(3), | ||
z: vec![1, 2, 3] | ||
} | ||
); | ||
assert_eq!( | ||
Foo::baz().y(5).z([6, 7, 8]).build(), | ||
Foo::Baz { | ||
y: Some(5), | ||
z: vec![6, 7, 8] | ||
} | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_skip() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum Foo { | ||
Bar { | ||
#[builder(default, setter(skip))] | ||
x: i32, | ||
}, | ||
Baz { | ||
#[builder(setter(strip_option))] | ||
y: Option<i32>, | ||
#[builder(default = y.into_iter().collect(), setter(skip))] | ||
z: Vec<i32>, | ||
}, | ||
} | ||
|
||
assert_eq!(Foo::bar().build(), Foo::Bar { x: 0 }); | ||
assert_eq!(Foo::baz().y(1).build(), Foo::Baz { y: Some(1), z: vec![1] }); | ||
} | ||
|
||
#[test] | ||
fn test_build_method_name() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
#[builder(doc, build_method(vis="", name=__build), field_defaults(default))] | ||
pub enum Foo { | ||
Bar { x: i32 }, | ||
Baz { y: i32 }, | ||
} | ||
|
||
assert_eq!(Foo::bar().x(1).__build(), Foo::Bar { x: 1 }); | ||
assert_eq!(Foo::baz().__build(), Foo::Baz { y: 0 }); | ||
} | ||
|
||
#[test] | ||
fn test_prefix_and_suffix() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
#[builder(field_defaults(setter(prefix = "with_")))] | ||
enum Foo { | ||
Bar { | ||
x: i32, | ||
}, | ||
#[builder(field_defaults(setter(suffix = "_value")))] | ||
Baz { | ||
y: i32, | ||
#[builder(setter(prefix = ""))] | ||
z: i32, | ||
}, | ||
} | ||
|
||
assert_eq!(Foo::bar().with_x(1).build(), Foo::Bar { x: 1 }); | ||
assert_eq!(Foo::baz().with_y_value(2).z_value(3).build(), Foo::Baz { y: 2, z: 3 }); | ||
} | ||
|
||
#[test] | ||
fn test_builder_method() { | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum Foo { | ||
BarBaz { | ||
x: i32, | ||
}, | ||
QuxHTTPQuux { | ||
y: i32, | ||
}, | ||
#[builder(builder_method(name = custom_builder))] | ||
Custom { | ||
z: i32, | ||
}, | ||
} | ||
|
||
assert_eq!(Foo::bar_baz().x(1).build(), Foo::BarBaz { x: 1 }); | ||
assert_eq!(Foo::qux_http_quux().y(2).build(), Foo::QuxHTTPQuux { y: 2 }); | ||
assert_eq!(Foo::custom_builder().z(3).build(), Foo::Custom { z: 3 }); | ||
} | ||
|
||
#[test] | ||
fn test_builder_type_visibility() { | ||
mod foo { | ||
use typed_builder::TypedBuilder; | ||
|
||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum Foo { | ||
#[builder(builder_type(vis="pub", name=CustomBuilder))] | ||
Bar { x: i32 }, | ||
} | ||
|
||
pub fn foo_bar_builder() -> CustomBuilder { | ||
Foo::bar() | ||
} | ||
|
||
pub fn build_and_get_x(builder: CustomBuilder, x: i32) -> i32 { | ||
let Foo::Bar { x } = builder.x(x).build(); | ||
x | ||
} | ||
} | ||
|
||
let builder: foo::CustomBuilder = foo::foo_bar_builder(); | ||
assert_eq!(foo::build_and_get_x(builder, 1), 1); | ||
} | ||
|
||
#[test] | ||
fn test_builder_on_enum_with_keywords() { | ||
#[allow(non_camel_case_types)] | ||
#[derive(PartialEq, Debug, TypedBuilder)] | ||
enum r#enum { | ||
Bar { r#type: i32 }, | ||
} | ||
|
||
assert_eq!(r#enum::bar().r#type(1).build(), r#enum::Bar { r#type: 1 }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's common for
enum
s to use these methods for returning anOption
that'sSome
iffself
's discriminant matches the method name (likeResult::ok
andResult::err
from the standard library). Using the same name for the builder will conflict with these methods.Wouldn't it be better to use
Foo::bar_qux_builder()
?