Crate macon

Source
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
  • Option fields are not mandatory. And setters use wrapped type.

§Settings

Settings are set using #[builder()] attribute.

§struct
§field

§Features

For any feature, you can find blueprints in ./tests directory showing code generated by macro.

§Typestate pattern (default)

Blueprints:

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:

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:

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::Option
  • std::option::Option
  • ::std::option::Option
  • Option

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:

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:

If field implements Default, it is then optional and value is:

  1. kept from default instance if struct derives Default,
  2. 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):

  • 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:

#[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 Panic or Result mode.
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.