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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! A macro to easily create immutable builders.
//!
//! The [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) is encouraged
//! in the Rust language, in particular as a strategy to emulate named and optional arguments,
//! which are intentionally not supported by Rust.
//! However creating all the builder machinery can lead to boilerplate code, in particular when the
//! generated data is complex and multi-layered.
//! The usual builder pattern is based on mutation and generally turns compile-time checks that
//! the final object is complete to a runtime verification. Assemblist allows you to create fluent
//! immutable builders structured as method chains like in:
//! ```rust
//! fn define_movie<'a>(name: &'a str)
//! .released_in(release_year: usize)
//! .directed_by(director_name: &'a str) -> Movie
//! {
//! Movie {
//! name: name.to_string(),
//! release_year,
//! director_name: director_name.to_string(),
//! }
//! }
//! ```
//! You can then just call it as it was declared:
//! ```rust
//! let movie = define_movie("The Lobster")
//! .released_in(2015)
//! .directed_by("Yorgos Lanthimos");
//! ```
use Tree;
use ;
use parse_macro_input;
/**
* The point of this crate. Allows to generate fluent immutable builders based on method chains.
*
* The argument of the `assemblist!` macro is a scope containing one or more method chains.
* A method chain is similar to a function except that its name and argument list are split into multiple sections.
* Behind the scene, `assemblist!` actually creates as many distinct methods and generates their respective result
* type automatically.
* ```rust
* assemblist! {
* fn define_movie<'a>(name: &'a str)
* .released_in(release_year: usize)
* .directed_by(director_name: &'a str) -> Movie
* {
* Movie {
* name: name.to_string(),
* release_year,
* director_name: director_name.to_string(),
* }
* }
* }
* ```
*
* You can then just call method chains as they are declared:
* ```rust
* let movie = define_movie("The Lobster")
* .released_in(2015)
* .directed_by("Yorgos Lanthimos");
* ```
*
* Multiple method chains can be declared inside the same `assemblist!{ … }` block:
* ```rust
* assemblist! {
* fn f1(/*args*/).f2(/*args*/).f3(/*args*/) { /* code */ }
* fn g1(/*args*/).g2(/*args*/) { /* code */ }
* fn h1(/*args*/).h2(/*args*/).h3(/*args*/).h4(/*args*/) { /* code */ }
* }
* ```
*
* ## Use builders with any function body
*
* Contrary to many alternative crates, in Assemblist, builder patterns are not generated from
* annotations on a struct to build, but by directly declaring method chains as if they were basic
* constructions of the Rust language. In * addition to making their use obvious, this pattern
* allows for much more flexibility in the implementation. In fact, you do not even need to build
* something. For example you may just want to decompose an existing function in order to clarify
* the purpose of its parameters:
* ```rust
* pub fn replace_in<'a>(text: &'a str)
* .occurrences_of(pattern: &'a str)
* .with(replacement: &'a str)
* .at_most(n: usize)
* .times() -> String
* {
* text.replacen(pattern, replacement, n)
* }
* ```
* You can actually include arbitrary complex code.
*
* ## Declare alternatives
*
* The builder pattern is a very expressive method to offer alternatives to users of a library.
* They can start with a common function name and then choose which subsequent method makes sense for
* their specific case. Assemblist aknowledges this possibility by offering the alternative syntax:
* ```rust
* fn new_http_request_to(url: Uri)
* .from<'a>(user_agent: &'a str)
* .with_authorization(authorization: HttpAuthorization).{
*
* fn as_get() -> GetHttpRequest {
* GetHttpRequest {
* url,
* user_agent: user_agent.to_string(),
* authorization,
* }
* }
*
* fn as_post().{
* fn with_text_body(body: String) -> PostHttpRequest {
* PostHttpRequest {
* url,
* user_agent: user_agent.to_string(),
* authorization,
* body: HttpBody::Text(body),
* }
* }
*
* fn with_json_body(json: JsonValue) -> PostHttpRequest {
* PostHttpRequest {
* url,
* user_agent: user_agent.to_string(),
* authorization,
* body: HttpBody::Json(json),
* }
* }
* }
* }
* ```
* Chaining with a `.{ … }` block gives you the possibility to define alternatives. Inside such a block,
* each possible continuation starts with the `fn` keyword and can itself be a method chain, possibly
* including other alternatives recursively. Each branch of the corresponding tree of method chains
* can provide a distinct implementation and even return a distinct type:
* ```rust
* let get_request = new_http_request_to(Uri::from_static("http://www.croco-paradise.tv"))
* .from("FireFox")
* .with_authorization(HttpAuthorization::None)
* .as_get();
*
* let post_request = new_http_request_to(Uri::from_static("http://www.croco-paradise.tv"))
* .from("FireFox")
* .with_authorization(HttpAuthorization::Bearer("sometoken3456=".to_string()))
* .as_post()
* .with_text_body("Hello world".to_string());
* ```
*
* ## Use method chains in inherent implementations
*
* You can either declare method chains as root items, as shown in previous examples, or declare them
* inside inherent implementations:
* ```rust
* struct Calculation;
*
* assemblist! {
* impl Calculation {
* fn add(a: isize).to(b: isize) -> isize { a + b }
* fn remove(a: isize).from(b: isize) -> isize { a - b }
* }
* }
* ```
* It is even possible to declare multiple inherent implementations inside the same `assemblist!`
* macro invocation and to mix them with root method chains.
*
* ## Current limitations
*
* ### No implicit lifetimes
*
* Assemblist does not handle implicit lifetimes for now so you must declare them explicitely. Each method
* inside the method chain can carry its own lifetimes:
* ```rust
* fn pour_items_from<'a, T>(source: &'a mut Vec<T>)
* .starting_at(n: usize)
* .into<'b>(destination: &'b mut Vec<T>)
* {
* for item in source.drain(n..) {
* destination.push(item);
* }
* }
* ```
*
* ### No patterns for arguments
*
* You cannot use patterns for methods arguments as in `f((a, b): (usize, bool))` or in `g(ref r: f64)`. If
* you need to do this, just use plain argument names and destructure them inside the body:
* ```rust
* fn f(pair: (usize, bool)).g(x: f64) {
* let (a, b) = pair;
* let ref r = x;
* }
* ```
*
* ### No `self` arguments
*
* For now, method chains inside inherent implementations cannot declare a `self` argument. Only "static"
* methods are allowed. So this example fails to compile:
* ```compile_fail
* struct MyInt(isize);
*
* assemblist! {
* impl MyInt {
* fn is_between(&self, a: isize).and(b: isize) { a <= self.0 && self.0 <= b }
* }
* }
* ```
*/