Crates.io | macon |
lib.rs | macon |
version | 1.2.0 |
source | src |
created_at | 2023-12-11 22:09:34.124458 |
updated_at | 2024-09-15 13:33:31.415239 |
description | Another builder macro-based generator with its own idioms. |
homepage | https://github.com/loganmzz/macon-rs |
repository | https://github.com/loganmzz/macon-rs |
max_upload_size | |
id | 1065606 |
size | 548,967 |
Another builder macro-based generator with its own idioms.
"Maçon" is French translation for "builder"
#[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();
<TargetStruct>Builder
)Default
builder()
function to target struct to initialize a new builderbuild()
function to create new target struct instancebuild()
call not compile (default)Into
Option
fields are not mandatory. And setters use wrapped type.Settings are set using #[builder()]
attribute.
mode=<value>
Change builder and associated build()
function behavior. Supported values: Typestate
(default), Panic
or Result
.
Default=!
Disable automatic Default
derive detection for struct. See "Default
struct".
Default
Enforce Default
support for struct. See "Default
struct".
Option=!
(deprecated. Use fields(Option)
instead.)
Into=!
(deprecated. Use fields(Into)
instead.)
fields(Option=!)
Disable automatic Option
detection for fields. See "Option
fields".
fields(Default=!)
Disable automatic Default
detection for fields. See "Default
fields".
fields(Into=!)
Disable Into
for fields. See "Into
argument".
Option=!
Disable automatic Option
detection for given field. Generated setter will rely on declared field type. See "Option
fields".
Option=WrappedType
Enforce Option
support for given field. Generated setter will rely on WrappedType
. See "Option
fields".
Default=!
Disable automatic Default
detection for given field. See "Default
fields".
Default
Enforce Default
support for given field. See "Default
fields".
Into=!
Disable Into
for setter. See "Into
argument".
For any feature, you can find blueprints in ./tests
directory showing code generated by macro.
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()
when all properties has been setOptionally, you can set it explictly:
#[macro_use] extern crate macon;
#[derive(Builder)]
#[builder(mode=Typestate)]
struct MyType {
integer: i32,
string: String,
}
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();
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(|_| ())
);
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
argumentBlueprints:
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();
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
fieldsBlueprints:
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
structBlueprints:
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
fieldsBlueprints:
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:
Default
,#[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();