1 | use std::iter; |
2 | |
3 | use proc_macro2::TokenStream; |
4 | use quote::{quote, ToTokens}; |
5 | use syn::{parse::Result, DeriveInput}; |
6 | |
7 | use crate::utils::{add_extra_generic_param, AttrParams, MultiFieldData, State}; |
8 | |
9 | /// Provides the hook to expand `#[derive(Into)]` into an implementation of `Into` |
10 | pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> { |
11 | let state = State::with_attr_params( |
12 | input, |
13 | trait_name, |
14 | quote!(::core::convert), |
15 | trait_name.to_lowercase(), |
16 | AttrParams { |
17 | enum_: vec!["ignore" , "owned" , "ref" , "ref_mut" ], |
18 | variant: vec!["ignore" , "owned" , "ref" , "ref_mut" ], |
19 | struct_: vec!["ignore" , "owned" , "ref" , "ref_mut" , "types" ], |
20 | field: vec!["ignore" ], |
21 | }, |
22 | )?; |
23 | let MultiFieldData { |
24 | variant_info, |
25 | field_types, |
26 | field_idents, |
27 | input_type, |
28 | .. |
29 | } = state.enabled_fields_data(); |
30 | |
31 | let mut tokens = TokenStream::new(); |
32 | |
33 | for ref_type in variant_info.ref_types() { |
34 | let reference = ref_type.reference(); |
35 | let lifetime = ref_type.lifetime(); |
36 | let reference_with_lifetime = ref_type.reference_with_lifetime(); |
37 | |
38 | let generics_impl; |
39 | let (_, ty_generics, where_clause) = input.generics.split_for_impl(); |
40 | let (impl_generics, _, _) = if ref_type.is_ref() { |
41 | generics_impl = add_extra_generic_param(&input.generics, lifetime); |
42 | generics_impl.split_for_impl() |
43 | } else { |
44 | input.generics.split_for_impl() |
45 | }; |
46 | |
47 | let additional_types = variant_info.additional_types(ref_type); |
48 | for explicit_type in iter::once(None).chain(additional_types.iter().map(Some)) { |
49 | let into_types: Vec<_> = field_types |
50 | .iter() |
51 | .map(|field_type| { |
52 | // No, `.unwrap_or()` won't work here, because we use different types. |
53 | if let Some(type_) = explicit_type { |
54 | quote! { #reference_with_lifetime #type_ } |
55 | } else { |
56 | quote! { #reference_with_lifetime #field_type } |
57 | } |
58 | }) |
59 | .collect(); |
60 | |
61 | let initializers = field_idents.iter().map(|field_ident| { |
62 | if let Some(type_) = explicit_type { |
63 | quote! { <#reference #type_>::from(#reference original.#field_ident) } |
64 | } else { |
65 | quote! { #reference original.#field_ident } |
66 | } |
67 | }); |
68 | |
69 | (quote! { |
70 | #[automatically_derived] |
71 | impl#impl_generics ::core::convert::From<#reference_with_lifetime #input_type#ty_generics> for |
72 | (#(#into_types),*) #where_clause { |
73 | |
74 | #[inline] |
75 | fn from(original: #reference_with_lifetime #input_type#ty_generics) -> Self { |
76 | (#(#initializers),*) |
77 | } |
78 | } |
79 | }).to_tokens(&mut tokens); |
80 | } |
81 | } |
82 | Ok(tokens) |
83 | } |
84 | |