Crate builder_macro [] [src]

This crate contains a builder! macro to declare a struct and a corresponding builder.

The macro is inspired from jadpole/builder-macro, and is designed to remove duplication of field declaration, as well as generating appropriate setter methods.

Specify the dependency in your crate's Cargo.toml:

[dependencies]
builder_macro = "0.3.0"

Include the macro inside your crate's lib.rs or main.rs.

#[macro_use]
extern crate builder_macro;

Examples

Non-consuming Builder

The simplest usage of the builder macro to generate a non-consuming builder is:

builder!(BuilderName -> StructName {
    fieldname: Type = Some(default_value), // or None if there is no sensible default
});

The above will generate a module private struct and a non-consuming builder with a single private field.

For example, given the following declaration:

builder!(BuilderName -> StructName {
    value: i32 = Some(1),
});

The generated code will function as follows:

struct StructName {
    value: i32,
}

/// Auto-generated builder
struct BuilderName {
    value: Option<i32>,
}

impl BuilderName {
    /// Construct the builder
    pub fn new() -> BuilderName { BuilderName { value: Some(1), } }

    /// Build the struct
    pub fn build(&self) -> Result<StructName, &'static str> {
        let value = try!(self.value.clone()
            .ok_or(concat!("Must pass argument for field: '", stringify!(value), "'")));
        Ok(StructName { value: value, })
    }

    #[allow(dead_code)]
    /// Auto-generated setter
    pub fn value(&mut self, value: i32) -> &mut Self {
        self.value = Some(value);
        self
    }
}

To generate public structs and builders, see visbility.

Consuming Builder

To generate a consuming builder, instead of using ->, use => between the builder name and the target struct name.

trait Magic {
    fn abracadabra(&mut self) -> i32;
}
struct Dust {
    value: i32,
}
impl Magic for Dust {
    fn abracadabra(&mut self) -> i32 {
        self.value
    }
}

// Note: we use => instead of -> for the consuming variant of the builder
builder!(MyStructBuilder => MyStruct {
    field_trait: Box<Magic> = Some(Box::new(Dust { value: 1 })),
    field_vec: Vec<Box<Magic>> = Some(vec![Box::new(Dust { value: 2 })]),
});

let mut my_struct = MyStructBuilder::new().build().unwrap();

assert_eq!(my_struct.field_trait.abracadabra(), 1);
assert_eq!(my_struct.field_vec[0].abracadabra(), 2);

Visibility

Generate a builder and struct with module private visibility:

builder!(MyStructBuilder -> MyStruct {
    field_i32: i32 = Some(123),
    field_str: &'static str = Some("abc"),
});

let my_struct = MyStructBuilder::new()
    .field_i32(456)
    .build()
    .unwrap();
assert_eq!(my_struct.field_i32, 456);
assert_eq!(my_struct.field_str, "abc"); // uses default

Generate a builder and struct with public visibility:

mod inner {
    builder!(pub MyStructBuilder -> MyStruct {
        pub field_i32: i32 = Some(123),
        field_str: &'static str = Some("abc"),
    });
}

let my_struct = inner::MyStructBuilder::new()
    .field_i32(456)
    .build()
    .unwrap();
assert_eq!(my_struct.field_i32, 456);

// The next line will fail compilation if uncommented as field_str is private
// assert_eq!(my_struct.field_str, "abc");

Assertions

You may specify assertions after field declarations inside an assertions: { ... } block.

If an assertion fails, the build() method will return an Err(...).

builder! {
    pub BuilderName -> StructName {
        /// a_field is an i32 which must be between 0 and 100 inclusive
        pub a_field: i32 = Some(50),
        #[allow(dead_code)]
        a_private_field: &'static str = None,
    }, assertions: {
        assert!(a_field >= 0);
        assert!(a_field <= 100);
        // Yes you can assert on private fields
        assert!(!a_private_field.is_empty());
    }
}

let result_1 = BuilderName::new().a_private_field("non-empty string").build();
let result_2 = BuilderName::new().a_private_field("").build();

assert!(result_1.is_ok());
assert_eq!(result_2.err(),
           Some("assertion failed: 'assert!(! a_private_field . is_empty (  ))'"));

Full Usage Format

The full macro usage format is:

// We declare the builder insider a module simply to demonstrate scope
mod inner {
    builder! {
        /// StructName is an example struct.
        /// These docs are copied over to the generated struct.
        pub BuilderName -> StructName {
            /// a_field is an i32 which must be between 0 and 100 inclusive
            // the trailing comma is mandatory due to how the macro is parsed
            pub a_field: i32 = Some(50),

            // None means no default value, a value must be specified when building
            // meta attributes are copied over to the struct's fields
            #[allow(dead_code)]
            a_private_field: &'static str = None,
        }, assertions: {
            assert!(a_field >= 0);
            assert!(a_field <= 100);
            // Yes you can assert on private fields
            assert!(!a_private_field.is_empty());
        }
    }
}

let my_struct = inner::BuilderName::new()
    .a_private_field("I must set this to a non-empty string")
    .build()
    .unwrap();

assert_eq!(50, my_struct.a_field);

The above will be similar to writing the following:

mod inner {
    /// StructName is an example struct.
    /// These docs are copied over to the generated struct.
    pub struct StructName {
        /// a_field is an i32 which must be between 0 and 100 inclusive
        pub a_field: i32,
        #[allow(dead_code)]
        a_private_field: &'static str,
    }

    /// Auto-generated builder
    pub struct BuilderName {
        /// a_field is an i32 which must be between 0 and 100 inclusive
        a_field: Option<i32>,
        #[allow(dead_code)]
        a_private_field: Option<&'static str>,
    }

    impl BuilderName {
        /// Construct the builder
        pub fn new() -> BuilderName {
            BuilderName{a_field: Some(50), a_private_field: None,}
        }

        /// Build the struct
        pub fn build(&self) -> Result<StructName, &'static str> {
            let a_field = try!(self.a_field.clone().ok_or(
                concat!("Must pass argument for field: '", stringify!(a_field), "'") ));
            let a_private_field = try!(self.a_private_field.clone().ok_or(
                concat!("Must pass argument for field: '", stringify!(a_private_field), "'") ));

            use std::panic;
            try!(panic::catch_unwind(|| { assert!(a_field >= 0); }).or(
                Err(concat!("assertion failed: '",
                            stringify!( assert!(a_field >= 0) ),
                            "'")) ) );
            try!(panic::catch_unwind(|| { assert!(a_field <= 100); }).or(
                Err(concat!("assertion failed: '",
                            stringify!( assert!(a_field <= 100) ),
                            "'")) ) );
            try!(panic::catch_unwind(|| { assert!(!a_private_field.is_empty()); }).or(
                    Err(concat!("assertion failed: '",
                                stringify!( assert!(!a_private_field.is_empty()) ),
                                "'")) ) );

            Ok(StructName {
                a_field: a_field,
                a_private_field: a_private_field,
            })
        }

        #[allow(dead_code)]
        /// Auto-generated setter
        pub fn a_field(&mut self, value: i32) -> &mut Self {
            self.a_field = Some(value);
            self
        }

        #[allow(dead_code)]
        /// Auto-generated setter
        pub fn a_private_field(&mut self, value: &'static str)
         -> &mut Self {
            self.a_private_field = Some(value);
            self
        }
    }
}

let my_struct = inner::BuilderName::new()
    .a_private_field("I must set this to a non-empty string")
    .build()
    .unwrap();

assert_eq!(50, my_struct.a_field);

Macros

builder

Macro to declare a struct and a corresponding builder. See the module documentation for more.