1use proc_macro2::{Span, TokenStream};
2use quote::{ToTokens, TokenStreamExt};
3
4use crate::{change_span, BlockContents, BuilderPattern, DefaultExpression, DEFAULT_STRUCT_NAME};
5
6#[derive(Debug, Clone)]
36pub struct Initializer<'a> {
37 pub crate_root: &'a syn::Path,
39 pub field_ident: &'a syn::Ident,
41 pub field_enabled: bool,
43 pub builder_pattern: BuilderPattern,
45 pub default_value: Option<&'a DefaultExpression>,
49 pub use_default_struct: bool,
51 pub custom_error_type_span: Option<Span>,
61 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 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 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 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 OptionOrDefault,
157 Block(&'a BlockContents),
159 Move,
161}
162
163enum MatchNone<'a> {
165 DefaultTo {
167 expr: &'a DefaultExpression,
168 crate_root: &'a syn::Path,
169 },
170 UseDefaultStructField(&'a syn::Ident),
174 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 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
217enum 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(quote!(
227 Some(value) => value
228 )),
229 Self::Clone { crate_root } => tokens.append_all(quote!(
230 Some(ref value) => #crate_root::export::core::clone::Clone::clone(value)
231 )),
232 }
233 }
234}
235
236#[doc(hidden)]
239#[macro_export]
240macro_rules! default_initializer {
241 () => {
242 Initializer {
243 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}