Skip to content

Latest commit

 

History

History
808 lines (633 loc) · 21 KB

CRATES_IO.md

File metadata and controls

808 lines (633 loc) · 21 KB

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:

#[macro_use] extern crate macon;

#[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:

#[macro_use] extern crate macon;

use std::path::PathBuf;

#[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:

#[macro_use] extern crate macon;

use std::path::PathBuf;

#[derive(Builder)]
#[builder(mode=Result)]
struct MyType {
  integer: i32,
  string: String,
}

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

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,
);

// 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 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,
);

// 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):

#[macro_use] extern crate macon;

#[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:

#[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,
}

// Builder signature
# struct Builder;
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:

#[macro_use] extern crate macon;

#[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.

#[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,
  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:

#[macro_use] extern crate macon;

#[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:

#[macro_use] extern crate macon;

#[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:

#[macro_use] extern crate macon;

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:

#[macro_use] extern crate macon;

#[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.

#[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:

// 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.
#[macro_use] extern crate macon;

#[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):

#[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>,
}

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

// Don't compile
#[macro_use] extern crate macon;

#[derive(Builder)]
struct DisableDefaultOnField {
  #[builder(Default=!)]
  integer: usize,
}

DisableDefaultOnField::builder()
  .integer_default()
  .build();