6 releases (3 stable)
1.2.0 | Sep 15, 2024 |
---|---|
1.1.0 | Aug 26, 2024 |
1.0.1-beta.1 | Jul 14, 2024 |
1.0.1-beta.0 | Apr 7, 2024 |
0.2.0 | Dec 11, 2023 |
#241 in Rust patterns
1,815 downloads per month
Used in translators
340KB
338 lines
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
Option
fields 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),Panic
orResult
. -
Default=!
Disable automaticDefault
derive detection for struct. See "Default
struct". -
Default
EnforceDefault
support for struct. See "Default
struct". -
Option=!
(deprecated. Usefields(Option)
instead.) -
Into=!
(deprecated. Usefields(Into)
instead.) -
fields(Option=!)
Disable automaticOption
detection for fields. See "Option
fields". -
fields(Default=!)
Disable automaticDefault
detection for fields. See "Default
fields". -
fields(Into=!)
DisableInto
for fields. See "Into
argument".
field
-
Option=!
Disable automaticOption
detection for given field. Generated setter will rely on declared field type. See "Option
fields". -
Option=WrappedType
EnforceOption
support for given field. Generated setter will rely onWrappedType
. See "Option
fields". -
Default=!
Disable automaticDefault
detection for given field. See "Default
fields". -
Default
EnforceDefault
support for given field. See "Default
fields". -
Into=!
DisableInto
for setter. See "Into
argument".
Features
For any feature, you can find blueprints in ./tests
directory showing code generated by macro.
Typestate pattern (default)
Blueprints:
blueprint_typestate_named.rs
blueprint_typestate_tuple.rs
blueprint_typestate_option.rs
blueprint_typestate_default_field.rs
blueprint_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:
#[macro_use] extern crate macon;
#[derive(Builder)]
#[builder(mode=Typestate)]
struct MyType {
integer: i32,
string: String,
}
Panic on build()
Blueprints:
blueprint_panic_named.rs
blueprint_panic_tuple.rs
blueprint_panic_option.rs
blueprint_panic_default_field.rs
blueprint_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:
#[macro_use] extern crate macon;
use std::path::PathBuf;
#[derive(Builder)]
#[builder(mode=Panic)]
struct MyType {
integer: i32,
path: PathBuf,
}
let _mytype: MyType = MyType::builder()
.integer(42)
.build();
Result on build()
Blueprints:
blueprint_result_named.rs
blueprint_result_tuple.rs
blueprint_result_option.rs
blueprint_result_default_field.rs
blueprint_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
:
#[macro_use] extern crate macon;
use std::path::PathBuf;
#[derive(Builder)]
#[builder(mode=Result)]
struct MyType {
integer: i32,
string: String,
}
let myTypeResult: Result<MyType,String> = MyType::builder()
.integer(42)
.build();
assert_eq!(
Err(String::from("Field path is missing")),
myTypeResult.map(|_| ())
);
Tuple
Blueprints:
Tuples are struct with unamed fields. Then set<ordinal>()
is used as setter:
#[macro_use] extern crate macon;
#[derive(Builder)]
struct MyTuple(
i32,
Option<String>,
String,
);
let _mytuple: MyTuple = MyTuple::builder()
.set0(42)
.set2(String::from("foobar"))
.build();
Only for Typestate
mode, you can use set()
, none()
, keep()
and default()
calls to assign values in order:
#[macro_use] extern crate macon;
#[derive(Builder)]
struct MyTuple(
i32,
Option<String>,
String,
);
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
):
#[macro_use] extern crate macon;
#[derive(Builder)]
struct MyTuple(
String,
);
let _mytuple: MyTuple = MyTuple::builder()
.set("foobar")
.build();
You can disable Into
support by using #[builder(Into=!)]
at struct or field level:
#[macro_use] extern crate macon;
#[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,
}
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:
#[macro_use] extern crate macon;
#[derive(Builder)]
struct DynTrait {
#[builder(Into=!)]
function: Box<dyn Fn(usize) -> usize>,
}
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
.
#[macro_use] extern crate macon;
#[derive(Builder)]
struct MyStruct {
value: String,
};
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
:
#[macro_use] extern crate macon;
#[derive(Builder)]
struct WithOptional {
mandatory: String,
optional: Option<String>,
}
let built = WithOptional::builder()
.optional("optional value")
.mandatory("some value")
.build();
assert_eq!(Some(String::from("optional value")), built.optional);
You can set them explicitly to None
with <field>_none()
or none()
for ordered setter:
#[macro_use] extern crate macon;
#[derive(Builder)]
pub struct WithOptional {
mandatory: String,
optional: Option<String>,
}
let built = WithOptional::builder()
.optional_none()
.mandatory("some value")
.build();
assert_eq!(None, built.optional);
Note: In order to detect optional fields, field type name must match:
core::option::Option
::core::option::Option
std::option::Option
::std::option::Option
Option
You can disable Option
support by using #[builder(Option=!)]
at struct or field level:
#[macro_use] extern crate macon;
#[derive(Builder)]
#[builder(Option=!)]
struct DisableOptionStruct {
optional: Option<String>,
}
let built = DisableOptionStruct::builder()
.optional(Some(String::from("mandatory value")))
.build();
assert_eq!(Some(String::from("mandatory value")), built.optional);
If you use an alias, use #[builder(Option=<WrappedType>)]
at field level to enable Option
support:
#[macro_use] extern crate macon;
type OptString = Option<String>;
#[derive(Builder)]
struct AliasedOptionStruct {
#[builder(Option=String)]
optional: OptString,
}
let built = AliasedOptionStruct::builder()
.optional("aliased value")
.build();
assert_eq!(Some(String::from("aliased value")), built.optional);
Default
struct
Blueprints:
blueprint_typestate_default_struct.rs
blueprint_panic_default_struct.rs
blueprint_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.
#[macro_use] extern crate macon;
#[derive(Builder,)]
#[derive(PartialEq,Debug,)]
#[builder(Default,)]
struct DeriveDefaultStruct {
integer: usize,
string: String,
optional: Option<String>,
}
impl Default for DeriveDefaultStruct {
fn default() -> Self {
DeriveDefaultStruct {
integer: 42,
string: String::from("plop!"),
optional: Some(String::from("some")),
}
}
}
let built = DeriveDefaultStruct::builder()
.build();
assert_eq!(
DeriveDefaultStruct {
integer: 42,
string: String::from("plop!"),
optional: Some(String::from("some")),
},
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.
#[macro_use] extern crate macon;
#[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:
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.rs
blueprint_panic_default_field.rs
blueprint_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.
#[macro_use] extern crate macon;
#[derive(Builder)]
#[derive(Debug,PartialEq,)]
struct WithDefaultFields {
integer: usize,
string: String,
optional: Option<String>,
}
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):
#[macro_use] extern crate macon;
#[derive(Builder)]
#[derive(Debug,PartialEq,)]
struct WithDefaultFields {
integer: usize,
string: String,
optional: Option<String>,
}
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):
bool
char
f32
f64
i8
i16
i32
i64
i128
isize
str
u8
u16
u32
u64
u128
usize
std::string::String
core::option::Option
std::option::Option
std::vec::Vec
alloc::vec::Vec
std::collections::HashMap
std::collections::hash_map::HashMap
std::collections::HashSet
std::collections::hash_set::HashSet
If you use an alias or unsupported type, use #[builder(Default)]
at field level to enable Default
support:
#[macro_use] extern crate macon;
#[derive(Builder)]
#[derive(Debug,PartialEq,)]
struct ExplicitDefaultOnField {
#[builder(Default)]
boxed: Box<usize>,
}
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:
// Don't compile
#[macro_use] extern crate macon;
#[derive(Builder)]
struct DisableDefaultOnField {
#[builder(Default=!)]
integer: usize,
}
DisableDefaultOnField::builder()
.integer_default()
.build();