1use proc_macro2::{Span, TokenStream};
2use quote::{ToTokens, TokenStreamExt};
3use syn;
4use BuilderPattern;
5use DEFAULT_STRUCT_NAME;
6
7use crate::{change_span, BlockContents, DefaultExpression};
8
9/// Initializer for the target struct fields, implementing `quote::ToTokens`.
10///
11/// Lives in the body of `BuildMethod`.
12///
13/// # Examples
14///
15/// Will expand to something like the following (depending on settings):
16///
17/// ```rust,ignore
18/// # extern crate proc_macro2;
19/// # #[macro_use]
20/// # extern crate quote;
21/// # extern crate syn;
22/// # #[macro_use]
23/// # extern crate derive_builder_core;
24/// # use derive_builder_core::{DeprecationNotes, Initializer, BuilderPattern};
25/// # fn main() {
26/// # let mut initializer = default_initializer!();
27/// # initializer.default_value = Some("42".parse().unwrap());
28/// # initializer.builder_pattern = BuilderPattern::Owned;
29/// #
30/// # assert_eq!(quote!(#initializer).to_string(), quote!(
31/// foo: match self.foo {
32/// Some(value) => value,
33/// None => { 42 },
34/// },
35/// # ).to_string());
36/// # }
37/// ```
38#[derive(Debug, Clone)]
39pub struct Initializer<'a> {
40 /// Path to the root of the derive_builder crate.
41 pub crate_root: &'a syn::Path,
42 /// Name of the target field.
43 pub field_ident: &'a syn::Ident,
44 /// Whether the builder implements a setter for this field.
45 pub field_enabled: bool,
46 /// How the build method takes and returns `self` (e.g. mutably).
47 pub builder_pattern: BuilderPattern,
48 /// Default value for the target field.
49 ///
50 /// This takes precedence over a default struct identifier.
51 pub default_value: Option<&'a DefaultExpression>,
52 /// Whether the build_method defines a default struct.
53 pub use_default_struct: bool,
54 /// Span where the macro was told to use a preexisting error type, instead of creating one,
55 /// to represent failures of the `build` method.
56 ///
57 /// An initializer can force early-return if a field has no set value and no default is
58 /// defined. In these cases, it will convert from `derive_builder::UninitializedFieldError`
59 /// into the return type of its enclosing `build` method. That conversion is guaranteed to
60 /// work for generated error types, but if the caller specified an error type to use instead
61 /// they may have forgotten the conversion from `UninitializedFieldError` into their specified
62 /// error type.
63 pub custom_error_type_span: Option<Span>,
64 /// Method to use to to convert the builder's field to the target field
65 ///
66 /// For sub-builder fields, this will be `build` (or similar)
67 pub conversion: FieldConversion<'a>,
68}
69
70impl<'a> ToTokens for Initializer<'a> {
71 fn to_tokens(&self, tokens: &mut TokenStream) {
72 let struct_field = &self.field_ident;
73 let builder_field = struct_field;
74
75 // This structure prevents accidental failure to add the trailing `,` due to incautious `return`
76 let append_rhs = |tokens: &mut TokenStream| {
77 if !self.field_enabled {
78 let default = self.default();
79 tokens.append_all(quote!(
80 #default
81 ));
82 } else {
83 match &self.conversion {
84 FieldConversion::Block(conv) => {
85 conv.to_tokens(tokens);
86 }
87 FieldConversion::Move => tokens.append_all(quote!( self.#builder_field )),
88 FieldConversion::OptionOrDefault => {
89 let match_some = self.match_some();
90 let match_none = self.match_none();
91 tokens.append_all(quote!(
92 match self.#builder_field {
93 #match_some,
94 #match_none,
95 }
96 ));
97 }
98 }
99 }
100 };
101
102 tokens.append_all(quote!(#struct_field:));
103 append_rhs(tokens);
104 tokens.append_all(quote!(,));
105 }
106}
107
108impl<'a> Initializer<'a> {
109 /// To be used inside of `#struct_field: match self.#builder_field { ... }`
110 fn match_some(&'a self) -> MatchSome {
111 match self.builder_pattern {
112 BuilderPattern::Owned => MatchSome::Move,
113 BuilderPattern::Mutable | BuilderPattern::Immutable => MatchSome::Clone {
114 crate_root: self.crate_root,
115 },
116 }
117 }
118
119 /// To be used inside of `#struct_field: match self.#builder_field { ... }`
120 fn match_none(&'a self) -> MatchNone<'a> {
121 match self.default_value {
122 Some(expr) => MatchNone::DefaultTo {
123 expr,
124 crate_root: self.crate_root,
125 },
126 None => {
127 if self.use_default_struct {
128 MatchNone::UseDefaultStructField(self.field_ident)
129 } else {
130 MatchNone::ReturnError {
131 crate_root: self.crate_root,
132 field_name: self.field_ident.to_string(),
133 span: self.custom_error_type_span,
134 }
135 }
136 }
137 }
138 }
139
140 fn default(&'a self) -> TokenStream {
141 let crate_root = self.crate_root;
142 match self.default_value {
143 Some(expr) => expr.with_crate_root(crate_root).into_token_stream(),
144 None if self.use_default_struct => {
145 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
146 let field_ident = self.field_ident;
147 quote!(#struct_ident.#field_ident)
148 }
149 None => {
150 quote!(#crate_root::export::core::default::Default::default())
151 }
152 }
153 }
154}
155
156#[derive(Debug, Clone)]
157pub enum FieldConversion<'a> {
158 /// Usual conversion: unwrap the Option from the builder, or (hope to) use a default value
159 OptionOrDefault,
160 /// Custom conversion is a block contents expression
161 Block(&'a BlockContents),
162 /// Custom conversion is just to move the field from the builder
163 Move,
164}
165
166/// To be used inside of `#struct_field: match self.#builder_field { ... }`
167enum MatchNone<'a> {
168 /// Inner value must be a valid Rust expression
169 DefaultTo {
170 expr: &'a DefaultExpression,
171 crate_root: &'a syn::Path,
172 },
173 /// Inner value must be the field identifier
174 ///
175 /// The default struct must be in scope in the build_method.
176 UseDefaultStructField(&'a syn::Ident),
177 /// Inner value must be the field name
178 ReturnError {
179 crate_root: &'a syn::Path,
180 field_name: String,
181 span: Option<Span>,
182 },
183}
184
185impl<'a> ToTokens for MatchNone<'a> {
186 fn to_tokens(&self, tokens: &mut TokenStream) {
187 match *self {
188 MatchNone::DefaultTo { expr, crate_root } => {
189 let expr = expr.with_crate_root(crate_root);
190 tokens.append_all(quote!(None => #expr));
191 }
192 MatchNone::UseDefaultStructField(field_ident) => {
193 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
194 tokens.append_all(quote!(
195 None => #struct_ident.#field_ident
196 ))
197 }
198 MatchNone::ReturnError {
199 ref field_name,
200 ref span,
201 crate_root,
202 } => {
203 let conv_span = span.unwrap_or_else(Span::call_site);
204 // If the conversion fails, the compiler error should point to the error declaration
205 // rather than the crate root declaration, but the compiler will see the span of #crate_root
206 // and produce an undesired behavior (possibly because that's the first span in the bad expression?).
207 // Creating a copy with deeply-rewritten spans preserves the desired error behavior.
208 let crate_root = change_span(crate_root.into_token_stream(), conv_span);
209 let err_conv = quote_spanned!(conv_span => #crate_root::export::core::convert::Into::into(
210 #crate_root::UninitializedFieldError::from(#field_name)
211 ));
212 tokens.append_all(quote!(
213 None => return #crate_root::export::core::result::Result::Err(#err_conv)
214 ));
215 }
216 }
217 }
218}
219
220/// To be used inside of `#struct_field: match self.#builder_field { ... }`
221enum MatchSome<'a> {
222 Move,
223 Clone { crate_root: &'a syn::Path },
224}
225
226impl ToTokens for MatchSome<'_> {
227 fn to_tokens(&self, tokens: &mut TokenStream) {
228 match *self {
229 Self::Move => tokens.append_all(iter:quote!(
230 Some(value) => value
231 )),
232 Self::Clone { crate_root: &Path } => tokens.append_all(iter:quote!(
233 Some(ref value) => #crate_root::export::core::clone::Clone::clone(value)
234 )),
235 }
236 }
237}
238
239/// Helper macro for unit tests. This is _only_ public in order to be accessible
240/// from doc-tests too.
241#[doc(hidden)]
242#[macro_export]
243macro_rules! default_initializer {
244 () => {
245 Initializer {
246 // Deliberately don't use the default value here - make sure
247 // that all test cases are passing crate_root through properly.
248 crate_root: &parse_quote!(::db),
249 field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
250 field_enabled: true,
251 builder_pattern: BuilderPattern::Mutable,
252 default_value: None,
253 use_default_struct: false,
254 conversion: FieldConversion::OptionOrDefault,
255 custom_error_type_span: None,
256 }
257 };
258}
259
260#[cfg(test)]
261mod tests {
262 #[allow(unused_imports)]
263 use super::*;
264
265 #[test]
266 fn immutable() {
267 let mut initializer = default_initializer!();
268 initializer.builder_pattern = BuilderPattern::Immutable;
269
270 assert_eq!(
271 quote!(#initializer).to_string(),
272 quote!(
273 foo: match self.foo {
274 Some(ref value) => ::db::export::core::clone::Clone::clone(value),
275 None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into(
276 ::db::UninitializedFieldError::from("foo")
277 )),
278 },
279 )
280 .to_string()
281 );
282 }
283
284 #[test]
285 fn mutable() {
286 let mut initializer = default_initializer!();
287 initializer.builder_pattern = BuilderPattern::Mutable;
288
289 assert_eq!(
290 quote!(#initializer).to_string(),
291 quote!(
292 foo: match self.foo {
293 Some(ref value) => ::db::export::core::clone::Clone::clone(value),
294 None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into(
295 ::db::UninitializedFieldError::from("foo")
296 )),
297 },
298 )
299 .to_string()
300 );
301 }
302
303 #[test]
304 fn owned() {
305 let mut initializer = default_initializer!();
306 initializer.builder_pattern = BuilderPattern::Owned;
307
308 assert_eq!(
309 quote!(#initializer).to_string(),
310 quote!(
311 foo: match self.foo {
312 Some(value) => value,
313 None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into(
314 ::db::UninitializedFieldError::from("foo")
315 )),
316 },
317 )
318 .to_string()
319 );
320 }
321
322 #[test]
323 fn default_value() {
324 let mut initializer = default_initializer!();
325 let default_value = DefaultExpression::explicit::<syn::Expr>(parse_quote!(42));
326 initializer.default_value = Some(&default_value);
327
328 assert_eq!(
329 quote!(#initializer).to_string(),
330 quote!(
331 foo: match self.foo {
332 Some(ref value) => ::db::export::core::clone::Clone::clone(value),
333 None => { 42 },
334 },
335 )
336 .to_string()
337 );
338 }
339
340 #[test]
341 fn default_struct() {
342 let mut initializer = default_initializer!();
343 initializer.use_default_struct = true;
344
345 assert_eq!(
346 quote!(#initializer).to_string(),
347 quote!(
348 foo: match self.foo {
349 Some(ref value) => ::db::export::core::clone::Clone::clone(value),
350 None => __default.foo,
351 },
352 )
353 .to_string()
354 );
355 }
356
357 #[test]
358 fn setter_disabled() {
359 let mut initializer = default_initializer!();
360 initializer.field_enabled = false;
361
362 assert_eq!(
363 quote!(#initializer).to_string(),
364 quote!(foo: ::db::export::core::default::Default::default(),).to_string()
365 );
366 }
367
368 #[test]
369 fn no_std() {
370 let initializer = default_initializer!();
371
372 assert_eq!(
373 quote!(#initializer).to_string(),
374 quote!(
375 foo: match self.foo {
376 Some(ref value) => ::db::export::core::clone::Clone::clone(value),
377 None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into(
378 ::db::UninitializedFieldError::from("foo")
379 )),
380 },
381 )
382 .to_string()
383 );
384 }
385}
386