1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! The `builder_method!` macro module.

/// A macro for simplifying implementation of methods for the `builder pattern`.
///
/// See the [`builder_methods! docs](./macro.builder_methods!.html) for more background and details.
#[macro_export]
macro_rules! builder_method {

    // A public builder method that assigns the given `$Type` value to the optional `$assignee`.
    (pub $fn_name:ident { $($assignee:ident).+ = Some($Type:ty) }) => {
        /// Build the type's self.$($assignee).+ with the given $Type.
        #[inline]
        pub fn $fn_name(mut self, $fn_name: $Type) -> Self {
            self.$($assignee).+ = Some($fn_name);
            self
        }
    };

    // A builder method that assigns the given `$Type` value to the optional `$assignee`.
    ($fn_name:ident { $($assignee:ident).+ = Some($Type:ty) }) => {
        /// Build the type's self.$($assignee).+ with the given $Type.
        #[inline]
        fn $fn_name(mut self, $fn_name: $Type) -> Self {
            self.$($assignee).+ = Some($fn_name);
            self
        }
    };

    // A public builder method that assigns the given `$Type` value to the `$assignee`.
    (pub $fn_name:ident { $($assignee:ident).+ = $Type:ty }) => {
        /// Build the type's self.$($assignee).+ with the given $Type.
        #[inline]
        pub fn $fn_name(mut self, $fn_name: $Type) -> Self {
            self.$($assignee).+ = $fn_name;
            self
        }
    };

    // A builder method that assigns the given `$Type` value to the `$assignee`.
    ($fn_name:ident { $($assignee:ident).+ = $Type:ty }) => {
        /// Build the type's self.$($assignee).+ with the given $Type.
        #[inline]
        fn $fn_name(mut self, $fn_name: $Type) -> Self {
            self.$($assignee).+ = $fn_name;
            self
        }
    };

}

/// A macro to simplify implementation of
/// ["builder-pattern"](https://en.wikipedia.org/wiki/Builder_pattern) methods.
///
/// The Builder Pattern
/// ===================
///
/// Conrod (and much of the Rust ecosystem) makes extensive use of the builder pattern in order to
/// provide an expressive widget API. After much iteration, we settled upon the builder pattern as
/// the best approach to interacting with highly optional types, or in our case, widgets.
///
/// Almost all widgets implement at least a few methods in order to take advantage of this pattern.
/// We call them "builder methods".
///
/// The builder pattern looks like this:
///
/// ```
/// # extern crate conrod_core;
/// # use conrod_core::color::{Color, BLACK, LIGHT_PURPLE};
///
/// struct Button {
///     color: Option<Color>,
/// }
///
/// impl Button {
///
///     /// Construct a default Button.
///     pub fn new() -> Self {
///         Button { color: None }
///     }
///     
///     /// A Color "builder method".
///     ///
///     /// Builds the Button with the given Color.
///     pub fn color(mut self, color: Color) -> Self {
///         self.color = Some(color);
///         self
///     }
///
/// }
///
/// fn main() {
///     // Here we build a purple button.
///     let purple_button = Button::new().color(LIGHT_PURPLE);
///     assert!(button_color(&purple_button) == LIGHT_PURPLE);
///
///     // Here we build a button with some default colour (which in our case is BLACK).
///     let button = Button::new();
///     assert!(button_color(&button) == BLACK);
/// }
///
/// // A function that returns a button's color or some default if the button's color is `None`.
/// fn button_color(button: &Button) -> Color {
///     button.color.unwrap_or(BLACK)
/// }
/// ```
///
/// This allows us to support large numbers of optionally specified parameters on widgets, rather
/// than forcing a user to give them all as `Option` arguments to some function.
///
/// builder_method!
/// ================
///
/// This macro allows you to easily implement any number of builder methods for either trait or
/// direct implementations.
///
/// Here's what implementing the color method for our `Button` now looks like:
///
/// ```
/// # #[macro_use] extern crate conrod_core;
/// # use conrod_core::color::Color;
/// # struct Button { color: Option<Color> }
/// # fn main() {}
/// impl Button {
///     builder_method!(pub color { color = Some(Color) });
/// }
/// ```
///
/// Breaking it down
/// ----------------
///
/// - The first `color` is an `ident` which specifies the name of the builder function. The
/// preceding `pub` visiblity token is optional.
/// - The second `color` is the field of `self` to which we assign the given value when building.
/// - `Color` is the type which the builder method receives as an argument. The encapsulating
/// `Some(*)` is optional, and can be removed for cases where the field itself is a normal type and
/// not an `Option` type.
///
/// Multiple `builder_methods!`
/// ---------------------------
///
/// We can also use the macro to implement multiple builder methods at once. The following is an
/// example of this directly from conrod's `Tabs` widget implementation. It expands to 9 unique
/// builder methods - one for every line.
///
/// ```txt
/// builder_methods!{
///     pub bar_width { style.maybe_bar_width = Some(Scalar) }
///     pub starting_tab_idx { maybe_starting_tab_idx = Some(usize) }
///     pub label_color { style.maybe_label_color = Some(Color) }
///     pub label_font_size { style.maybe_label_font_size = Some(FontSize) }
///     pub canvas_style { style.canvas = canvas::Style }
///     pub pad_left { style.canvas.pad_left = Some(Scalar) }
///     pub pad_right { style.canvas.pad_right = Some(Scalar) }
///     pub pad_bottom { style.canvas.pad_bottom = Some(Scalar) }
///     pub pad_top { style.canvas.pad_top = Some(Scalar) }
/// }
/// ```
///
/// Note that the `builder_methods!` macro is designed to work harmony with
/// [`widget_style!`][1] - a macro which simplifies implementation of a widget's associated `Style`
/// type. If you are designing your own widget and you haven't looked at it yet, we recommend you
/// [check out the docs][1].
///
/// [1]: ./macro.widget_style!.html
#[macro_export]
macro_rules! builder_methods {
    (pub $fn_name:ident { $($assignee:ident).+ = Some($Type:ty) } $($rest:tt)*) => {
        $crate::builder_method!(pub $fn_name { $($assignee).+ = Some($Type) });
        builder_methods!($($rest)*);
    };

    ($fn_name:ident { $($assignee:ident).+ = Some($Type:ty) } $($rest:tt)*) => {
        $crate::builder_method!($fn_name { $($assignee).+ = Some($Type) });
        builder_methods!($($rest)*);
    };
    
    (pub $fn_name:ident { $($assignee:ident).+ = $Type:ty } $($rest:tt)*) => {
        $crate::builder_method!(pub $fn_name { $($assignee).+ = $Type });
        builder_methods!($($rest)*);
    };

    ($fn_name:ident { $($assignee:ident).+ = $Type:ty } $($rest:tt)*) => {
        $crate::builder_method!($fn_name { $($assignee).+ = $Type });
        builder_methods!($($rest)*);
    };

    ($($rest:tt)*) => {};
}