Expand description
Another builder macro-based generator with its own idioms.
“Maçon” is French translation for “builder”
§Usage
#[macro_use] extern crate macon;
#[derive(Builder)]
struct MyType {
integer: i32,
string: String,
optional: Option<String>,
}
let _mytype: MyType = MyType::builder()
.integer(42)
.string("foobar")
.build();- adds a builder struct (
<TargetStruct>Builder) - build struct implements
Default - adds a
builder()function to target struct to initialize a new builder - each target struct field can be set with function of same name and parameter of same type
- use
build()function to create new target struct instance - any unset field will make
build()call not compile (default) - setter argument is generic over
Into Optionfields are not mandatory. And setters use wrapped type.
§Settings
Settings are set using #[builder()] attribute.
§struct
-
mode=<value>
Change builder and associatedbuild()function behavior. Supported values:Typestate(default),PanicorResult. -
Default=!
Disable automaticDefaultderive detection for struct. See “Defaultstruct”. -
Default
EnforceDefaultsupport for struct. See “Defaultstruct”. -
Option=!(deprecated. Usefields(Option=!)instead.) -
Into=!(deprecated. Usefields(Into=!)instead.) -
fields(Option=!)
Disable automaticOptiondetection for fields. See “Optionfields”. -
fields(Default=!)
Disable automaticDefaultdetection for fields. See “Defaultfields”. -
fields(Into=!)
DisableIntofor fields. See “Intoargument”.
§field
-
Option=!
Disable automaticOptiondetection for given field. Generated setter will rely on declared field type. See “Optionfields”. -
Option=WrappedType
EnforceOptionsupport for given field. Generated setter will rely onWrappedType. See “Optionfields”. -
Default=!
Disable automaticDefaultdetection for given field. See “Defaultfields”. -
Default
EnforceDefaultsupport for given field. See “Defaultfields”. -
Into=!
DisableIntofor setter. See “Intoargument”.
§Features
For any feature, you can find blueprints in ./tests directory showing code generated by macro.
§Typestate pattern (default)
Blueprints:
blueprint_typestate_named.rsblueprint_typestate_tuple.rsblueprint_typestate_option.rsblueprint_typestate_default_field.rsblueprint_typestate_default_struct.rs
By default, builder rely on typestate pattern. It means state is encoded in type (using generics). Applicable functions are implemented (callable) only when state (type) matches:
- Build function
build()when all properties has been set - Each property setter function as long as property haven’t been set
Optionally, you can set it explictly:
#[derive(Builder)]
#[builder(mode=Typestate)]
struct MyType {
integer: i32,
string: String,
}
// Builder signature
impl Builder {
fn integer(self, value: i32) -> Self
fn string(self, value: String) -> Self
fn build(self) -> MyType
}§Panic on build()
Blueprints:
blueprint_panic_named.rsblueprint_panic_tuple.rsblueprint_panic_option.rsblueprint_panic_default_field.rsblueprint_panic_default_struct.rs
By default, builder rely on typestate pattern to avoid misconfiguration by adding compilation constraint. You can switch to a builder that just panic when misconfigured:
#[derive(Builder)]
#[builder(mode=Panic)]
struct MyType {
integer: i32,
path: PathBuf,
}
// Builder signature
impl Builder {
fn integer(self, value: i32) -> Self
fn path(self, value: PathBuf) -> Self
fn build(self) -> MyType
}
let _mytype: MyType = MyType::builder()
.integer(42)
.build();§Result on build()
Blueprints:
blueprint_result_named.rsblueprint_result_tuple.rsblueprint_result_option.rsblueprint_result_default_field.rsblueprint_result_default_struct.rs
By default, builder rely on typestate pattern to avoid misconfiguration by adding compilation constraint. You can switch to a builder
that returns a Result:
#[derive(Builder)]
#[builder(mode=Result)]
struct MyType {
integer: i32,
path: PathBuf,
}
// Builder signature
impl Builder {
fn integer(self, value: i32) -> Self
fn path(self, value: PathBuf) -> Self
fn build(self) -> Result<MyType, String>
}
let myTypeResult: Result<MyType,String> = MyType::builder()
.integer(42)
.build();
assert_eq!(
Err(String::from("Field path is missing")),
myTypeResult.map(|_| ())
);§Tuple
Blueprints:
A tuple is a struct with unamed fields. Then set<ordinal>() is used as setter:
#[derive(Builder)]
struct MyTuple(
i32,
Option<String>,
String,
);
// Builder signature
impl Builder {
fn set0(self, value: i32) -> Self
fn set1(self, value: String) -> Self
fn set2(self, value: String) -> Self
fn build(self) -> MyTuple
}
let _mytuple: MyTuple = MyTuple::builder()
.set0(42)
.set2(String::from("foobar"))
.build();Only for Typestate mode, you can chain set(), none(), keep() and default() calls to assign values in order:
// Builder signature
impl Builder0 {
fn set(self, value: i32) -> Builder1
}
impl Builder1 {
fn set(self, value: String) -> Builder2
fn none(self) -> Builder2
}
impl Builder2 {
fn set(self, value: String) -> Builder
}
impl Builder {
fn build(self) -> MyTuple
}
let _mytuple: MyTuple = MyTuple::builder()
.set(42)
.none()
.set(String::from("foobar"))
.build();§Into argument
Blueprints:
Setter function argument is generic over Into to ease conversion (especially for &str):
#[derive(Builder)]
struct MyTuple(
String,
);
// Builder signature
impl Builder0 {
fn set<V: Into<String>>(self, value: V) -> Builder
}
impl Builder {
fn build(self) -> MyTuple
}
let _mytuple: MyTuple = MyTuple::builder()
.set("foobar")
.build();You can disable Into support by using #[builder(Into=!)] at struct or field level:
#[derive(Builder)]
#[builder(Into=!)] // Disable for all fields
struct IntoSettings {
#[builder(Into=!)] // Disable for specific field
no_into: String,
#[builder(Into)] // Enable (only when disabled at struct level) for specific field
with_into: String,
}
// Builder signature
impl Builder {
fn no_into(value: String) -> Self
fn with_into<V: Into<String>>(value: V) -> Self
fn build(self) -> IntoSettings
}
let built = IntoSettings::builder()
.no_into(String::from("no value conversion"))
.with_into("value conversion")
.build();
assert_eq!(String::from("no value conversion"), built.no_into);
assert_eq!(String::from("value conversion"), built.with_into);This feature is required to use with dyn trait:
#[derive(Builder)]
struct DynTrait {
#[builder(Into=!)]
function: Box<dyn Fn(usize) -> usize>,
}
// Builder signature
impl Builder {
fn function(self, value: Box<dyn Fn(usize) -> usize>) -> Self
fn build(self) -> DynTrait
}
DynTrait::builder()
.function(Box::new(|x| x + 1))
.build();§Implement Into
Blueprints:
Builders implement Into for target type (and reverse From also). Except for Result mode which uses TryInto / TryFrom.
let _mytuple: MyStruct = MyStruct::builder()
.value("foobar")
.into();§Option fields
Blueprints:
As their name suggests, Option fields are facultative: you can build instance without setting them explicitly.
Setter argument are still generic over Into but for wrapped type. No need to wrap into an Option:
#[derive(Builder)]
struct WithOptional {
mandatory: String,
discretionary: Option<String>,
}
// Builder signature
impl Builder {
fn mandatory(self, value: String) -> Self
fn discretionary(self, value: String) -> Self
fn build(self) -> WithOptional
}
let built = WithOptional::builder()
.discretionary("optional value")
.mandatory("some value")
.build();
assert_eq!(Some(String::from("optional value")), built.discretionary);You can set them explicitly to None with <field>_none() or none() for ordered setter:
#[derive(Builder)]
pub struct WithOptional {
mandatory: String,
discretionary: Option<String>,
}
// Builder signature
impl Builder {
fn mandatory<V: Into<String>>(self, value: V) -> Self
fn discretionary_none(self) -> Self
fn build(self) -> WithOptional
}
let built = WithOptional::builder()
.discretionary_none()
.mandatory("some value")
.build();
assert_eq!(None, built.discretionary);Note: In order to detect optional fields, field type name must match:
core::option::Option::core::option::Optionstd::option::Option::std::option::OptionOption
You can disable Option support by using #[builder(Option=!)] at struct or field level:
#[derive(Builder)]
#[builder(Option=!)]
struct DisableOptionStruct {
discretionary: Option<String>,
}
// Builder signature
impl Builder {
fn discretionary(self, value: Option<String>) -> Self
fn build(self) -> DisableOptionStruct
}
let built = DisableOptionStruct::builder()
.discretionary(Some(String::from("mandatory value")))
.build();
assert_eq!(Some(String::from("mandatory value")), built.discretionary);If you use an alias, use #[builder(Option=<WrappedType>)] at field level to enable Option support:
type OptString = Option<String>;
#[derive(Builder)]
struct AliasedOptionStruct {
#[builder(Option=String)]
discretionary: OptString,
}
// Builder signature
impl Builder {
fn discretionary(self, value: String) -> Self
fn build(self) -> AliasedOptionStruct
}
let built = AliasedOptionStruct::builder()
.discretionary("aliased value")
.build();
assert_eq!(Some(String::from("aliased value")), built.discretionary);If you are already dealing with Option, use <field>_optional or optional for ordered setter:
#[derive(Builder)]
struct WithOptional {
discretionary: Option<String>,
}
// Builder signature
impl Builder {
fn discretionary_optional(self, value: Option<String>) -> Self
fn build(self) -> Self
}
let discretionary = Some("any");
let built = WithOptional::builder()
.discretionary_optional(discretionary)
.build();
assert_eq!(Some(String::from("any")), built.discretionary);§Default struct
Blueprints:
blueprint_typestate_default_struct.rsblueprint_panic_default_struct.rsblueprint_result_default_struct.rs
If struct derives Default, all fields are then optional and values are kept from default instance:
Note: In order to detect Default derive, Builder derive attribute must be placed before other derive attributes.
#[derive(Builder,)]
#[derive(Default,PartialEq,Debug,)]
struct DeriveDefaultStruct {
integer: usize,
string: String,
optional: Option<String>,
}
let built = DeriveDefaultStruct::builder()
.build();
assert_eq!(
DeriveDefaultStruct {
integer: 0,
string: String::from(""),
optional: None,
},
built,
);In case Default derive detection is undesired, you can disable it with #[builder(Default=!)].
On the other hand, if have your own Default implementation, you can add #[builder(Default)] to enable support.
#[derive(Builder,)]
#[derive(PartialEq,Debug,)]
#[builder(Default,)]
struct CustomDefaultStruct {
integer: usize,
string: String,
optional: Option<String>,
}
impl Default for CustomDefaultStruct {
fn default() -> Self {
CustomDefaultStruct {
integer: 42,
string: String::from("plop!"),
optional: Some(String::from("some")),
}
}
}
let built = CustomDefaultStruct::builder()
.build();
assert_eq!(
CustomDefaultStruct {
integer: 42,
string: String::from("plop!"),
optional: Some(String::from("some")),
},
built,
);You can keep default value (from default built instance) explicitly with <field>_keep() or keep() for ordered setter:
// Builder signature
impl Builder {
fn integer<V: Into<usize>>(self, value: V) -> Self
fn integer_keep(self) -> Self
fn string<V: Into<String>>(self, value: V) -> Self
fn string_keep(self) -> Self
fn optional<V: Into<String>>(self, value: V) -> Self
fn optional_none(self) -> Self
fn optional_keep(self) -> Self
fn build(self) -> CustomDefaultStruct
}
let built = CustomDefaultStruct::builder()
.integer_keep()
.string("overriden")
.optional_none()
.build();
assert_eq!(
CustomDefaultStruct {
integer: 42,
string: String::from("overriden"),
optional: None,
},
built,
);§Default fields
Blueprints:
blueprint_typestate_default_field.rsblueprint_panic_default_field.rsblueprint_result_default_field.rs
If field implements Default, it is then optional and value is:
- kept from default instance if struct derives
Default, - or, initialized with default value.
#[derive(Builder)]
#[derive(Debug,PartialEq,)]
struct WithDefaultFields {
integer: usize,
string: String,
optional: Option<String>,
}
// Builder signature
impl Builder {
fn integer<V: Into<usize>>(self, value: V) -> Self
fn integer_default(self) -> Self
fn string<V: Into<String>>(self, value: V) -> Self
fn build(self) -> WithDefaultFields
}
let built = WithDefaultFields::builder()
.build();
assert_eq!(
WithDefaultFields {
integer: 0,
string: String::from(""),
optional: None,
},
built,
);You can set them explicitly to default with <field>_default() or default() for ordered setter (e.g. override default instance value):
let built = WithDefaultFields::builder()
.integer_default()
.string_default()
.optional_default()
.build();
assert_eq!(
WithDefaultFields {
integer: 0,
string: String::from(""),
optional: None,
},
built,
);In order to detect default fields, field type name must match (leading :: and module path are optionals):
boolcharf32f64i8i16i32i64i128isizestru8u16u32u64u128usizestd::string::Stringcore::option::Optionstd::option::Optionstd::vec::Vecalloc::vec::Vecstd::collections::HashMapstd::collections::hash_map::HashMapstd::collections::HashSetstd::collections::hash_set::HashSet
If you use an alias or unsupported type, use #[builder(Default)] at field level to enable Default support:
#[derive(Builder)]
#[derive(Debug,PartialEq,)]
struct ExplicitDefaultOnField {
#[builder(Default)]
boxed: Box<usize>,
}
// Builder signature
impl Builder {
fn boxed<V: Into<Box<usize>>>(self, value: V) -> Self
fn boxed_default(self) -> Self
fn build(self) -> ExplicitDefaultOnField
}
let built = ExplicitDefaultOnField::builder()
.build();
assert_eq!(
ExplicitDefaultOnField {
boxed: Box::from(0),
},
built,
);You can disable Default support by using #[builder(Default=!)] at field level:
#[derive(Builder)]
struct DisableDefaultOnField {
#[builder(Default=!)]
integer: usize,
}
DisableDefaultOnField::builder()
.integer_default()
.build();Enums§
- Building
- Builder field type for
PanicorResultmode. - Defaulting
- Buidler field type when target field implement
Default. - Keeping
- Builder field type when building struct implementing
Default.
Derive Macros§
- Builder
- Derive macro to generate builder for your structs. See crate documentation for usage.