default_struct_builder/
lib.rs

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
//! Generates builder methods of every field of a struct. It is meant to be used on structs that
//! implement `Default`. There is no separate builder struct generated and no need to call a
//! `build()` method at the end or `.unwrap()`.
//!
//! This crate is used by the crate `leptos-use` for the option structs that
//! can be passed to the various functions.
//!
//! ## Installation
//!
//! In your project folder run
//!
//! ```sh
//! cargo add default-struct-builder
//! ```
//!
//! ## Usage
//!
//! It is very easy to use:
//!
//! ```
//! use default_struct_builder::DefaultBuilder;
//!
//! #[derive(DefaultBuilder, Default)]
//! pub struct SomeOptions {
//!     throttle: f64,
//!
//!     #[builder(into)]
//!     offset: Option<f64>,
//!
//!     #[builder(skip)]
//!     not_included: u32,
//! }
//! ```
//!
//! you can then use the struct like this:
//!
//! ```
//! # use default_struct_builder::DefaultBuilder;
//! #
//! # #[derive(DefaultBuilder, Default)]
//! # pub struct SomeOptions {
//! #     throttle: f64,
//! #
//! #     #[builder(into)]
//! #     offset: Option<f64>,
//! #
//! #     #[builder(skip)]
//! #     not_included: u32,
//! # }
//! #
//! # fn main() {
//! let options = SomeOptions::default().offset(4.0);
//!
//! assert_eq!(options.offset, Some(4.0));
//! assert_eq!(options.throttle, 0.0);
//! assert_eq!(options.not_included, 0);
//! # }
//! ```
//!
//! ### Generics
//!
//! The macro is ready to be used on generic structs.
//!
//! ```
//! use default_struct_builder::DefaultBuilder;
//!
//! #[derive(DefaultBuilder, Default)]
//! pub struct SomeOptions<T>
//! where
//!     T: Default,
//! {
//!     some_field: T,
//! }
//! ```
//!
//! ### Doc comments
//!
//! All doc comments on fields are directly passed on to their generated setter methods.
//!
//! ## How it works
//!
//! The derive macro generates the following code:
//!
//! ```
//! # #[derive(Default)]
//! # pub struct SomeOptions {
//! #     throttle: f64,
//! #     offset: Option<f64>,
//! #     not_included: u32,
//! # }
//! #
//! impl SomeOptions {
//!     // setter methods are given that consume `self` and return a new `Self` with the field value changed
//!     pub fn throttle(self, value: f64) -> Self {
//!         Self {
//!             throttle: value,
//!             ..self
//!         }
//!     }
//!
//!     // because `into` was specified this method is generic and calls `.into()` when setting the value
//!     pub fn offset<T>(self, value: T) -> Self
//!     where
//!         T: Into<Option<f64>>,
//!     {
//!         Self {
//!             offset: value.into(),
//!             ..self
//!         }
//!     }
//!
//!     // no method for field `not_included` because `skip` was specified
//! }
//! ```
//!
//! ### Generics
//!
//! In the case of a generic field the generated method is a bit more complex because by calling
//! the method the type of the type parameter can be different than before.
//!
//! Let's look at the following example.
//!
//! ```
//! use default_struct_builder::DefaultBuilder;
//!
//! #[derive(DefaultBuilder, Default)]
//! pub struct SomeOptions<T>
//! where
//!     T: Default,
//! {
//!     some_field: T,
//!     other_field: i16,
//! }
//!
//! impl SomeOptions<f32> {
//!     pub fn new() -> Self {
//!         Self {
//!             some_field: 42.0,
//!             other_field: 0,
//!         }   
//!     }
//! }
//! #
//! # fn main() {
//! #    let options = SomeOptions::new().some_field("string");
//! # }
//! ```
//!
//! This generates the setter method below.
//!
//! ```
//! # pub struct SomeOptions<T>
//! # where
//! #     T: Default,
//! # {
//! #     some_field: T,
//! #     other_field: i16,
//! # }
//! #
//! # impl SomeOptions<f32> {
//! #     pub fn new() -> Self {
//! #         Self {
//! #             some_field: 42.0,
//! #             other_field: 0,
//! #         }   
//! #     }
//! # }
//! #
//! impl<T> SomeOptions<T>
//! where
//!     T: Default,
//! {
//!     pub fn some_field<NewT>(self, value: NewT) -> SomeOptions<NewT>
//!     where
//!         NewT: Default,
//!     {
//!         SomeOptions::<NewT> {
//!             some_field: value,
//!             other_field: self.other_field,
//!         }
//!     }
//! }
//!
//! fn main() {
//!    let options = SomeOptions::new()  // at first    SomeOptions<f32>
//!         .some_field("string");       // changed to  SomeOptions<&str>
//! }
//! ```
//!
//! In cases where you don't want a generic field to be able to change the generic type you
//! can annotate it with `keep_type`.
//!
//! ```
//! # use default_struct_builder::DefaultBuilder;
//! #
//! #[derive(DefaultBuilder)]
//! struct SomeOptions<T> {
//!     #[builder(keep_type)]
//!     the_field: T,
//! }
//! ```
//!
//! this will generate a standard builder method as if `T` wasn't generic.
//!
//! ### `Box`, `Rc` and `Arc`
//!
//! The macro detects if a field is a `Box` (or `Rc` or `Arc`) and generates a builder method that
//! accepts the inner type (without `Box` or `Rc` or `Arc`) and adds the outer type in the body.
//!
//! In case it's a `Box<dyn Trait>` the builder method will have an argument of type
//! `impl Trait`. The same goes for `Rc` and `Arc`.
//!
//! If you want to prevent this auto un-wrapping you can use the `#[builder(keep_outer)]` attribute.
//!
//! ```
//! # use std::rc::Rc;
//! # use default_struct_builder::DefaultBuilder;
//! #
//! trait Test {}
//!
//! #[derive(DefaultBuilder)]
//! struct SomeOptions {
//!     the_field: Box<dyn Test>,
//!     other_field: Rc<String>,
//!
//!     #[builder(keep_outer)]
//!     keep: Box<String>,
//! }
//! ```
//!
//! This will generate the following code:
//!
//! ```
//! # use std::rc::Rc;
//! # use default_struct_builder::DefaultBuilder;
//! #
//! # trait Test {}
//! #
//! # struct SomeOptions {
//! #     the_field: Box<dyn Test>,
//! #     other_field: Rc<String>,
//! #     keep: Box<String>,
//! # }
//! #
//! impl SomeOptions {
//!     pub fn the_field(self, value: impl Test + 'static) -> Self {
//!         Self {
//!             the_field: Box::new(value),
//!             ..self
//!         }   
//!     }
//!
//!     pub fn other_field(self, value: String) -> Self {
//!         Self {
//!             other_field: Rc::new(value),
//!             ..self
//!         }
//!     }
//!
//!     pub fn keep(self, value: Box<String>) -> Self {
//!         Self {
//!             keep: value,
//!             ..self
//!         }   
//!     }
//! }
//! ```
//!
//!
//! ## Related Work
//!
//! For more general purposes please check out the much more powerful
//! [`derive_builder` crate](https://github.com/colin-kiegel/rust-derive-builder).

mod builder;

use builder::DefaultBuilderDeriveInput;
use darling::FromDeriveInput;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::parse_macro_input;

#[proc_macro_derive(DefaultBuilder, attributes(builder))]
pub fn derive_builder(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    let data = DefaultBuilderDeriveInput::from_derive_input(&input);
    let stream = match data {
        Ok(data) => data.into_token_stream(),
        Err(err) => err.write_errors(),
    };
    stream.into()
}