derive_more/
into.rs

1use std::iter;
2
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{parse::Result, DeriveInput};
6
7use crate::utils::{add_extra_generic_param, AttrParams, MultiFieldData, State};
8
9/// Provides the hook to expand `#[derive(Into)]` into an implementation of `Into`
10pub 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}