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

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more