educe/trait_handlers/into/
into_struct.rs1use std::collections::HashMap;
2
3use quote::quote;
4use syn::{Data, DeriveInput, Field, Meta, Path, Type};
5
6use super::{
7 models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
8 TraitHandlerMultiple,
9};
10use crate::{common::ident_index::IdentOrIndex, Trait};
11
12pub(crate) struct IntoStructHandler;
13
14impl TraitHandlerMultiple for IntoStructHandler {
15 #[inline]
16 fn trait_meta_handler(
17 ast: &DeriveInput,
18 token_stream: &mut proc_macro2::TokenStream,
19 traits: &[Trait],
20 meta: &[Meta],
21 ) -> syn::Result<()> {
22 let type_attribute = TypeAttributeBuilder {
23 enable_types: true
24 }
25 .build_from_into_meta(meta)?;
26
27 if let Data::Struct(data) = &ast.data {
28 let fields = &data.fields;
29
30 let field_attributes: HashMap<usize, FieldAttribute> = {
31 let mut map = HashMap::new();
32
33 for (index, field) in fields.iter().enumerate() {
34 let field_attribute = FieldAttributeBuilder {
35 enable_types: true
36 }
37 .build_from_attributes(&field.attrs, traits)?;
38
39 for ty in field_attribute.types.keys() {
40 if !type_attribute.types.contains_key(ty) {
41 return Err(super::panic::no_into_impl(ty));
42 }
43 }
44
45 map.insert(index, field_attribute);
46 }
47
48 map
49 };
50
51 for (target_ty, bound) in type_attribute.types {
52 let mut into_types: Vec<&Type> = Vec::new();
53
54 let mut into_token_stream = proc_macro2::TokenStream::new();
55
56 let (index, field, method) = {
57 let fields = &data.fields;
58
59 if fields.len() == 1 {
60 let field = fields.into_iter().next().unwrap();
61
62 let method = if let Some(field_attribute) = field_attributes.get(&0) {
63 if let Some(method) = field_attribute.types.get(&target_ty) {
64 method.as_ref()
65 } else {
66 None
67 }
68 } else {
69 None
70 };
71
72 (0usize, field, method)
73 } else {
74 let mut into_field: Option<(usize, &Field, Option<&Path>)> = None;
75
76 for (index, field) in fields.iter().enumerate() {
77 if let Some(field_attribute) = field_attributes.get(&index) {
78 if let Some((key, method)) =
79 field_attribute.types.get_key_value(&target_ty)
80 {
81 if into_field.is_some() {
82 return Err(super::panic::multiple_into_fields(key));
83 }
84
85 into_field = Some((index, field, method.as_ref()));
86 }
87 }
88 }
89
90 if into_field.is_none() {
91 for (index, field) in fields.iter().enumerate() {
93 let field_ty = super::common::to_hash_type(&field.ty);
94
95 if target_ty.eq(&field_ty) {
96 if into_field.is_some() {
97 into_field = None;
99
100 break;
101 }
102
103 into_field = Some((index, field, None));
104 }
105 }
106 }
107
108 if let Some(into_field) = into_field {
109 into_field
110 } else {
111 return Err(super::panic::no_into_field(&target_ty));
112 }
113 }
114 };
115
116 let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
117
118 if let Some(method) = method {
119 into_token_stream.extend(quote!( #method(self.#field_name) ));
120 } else {
121 let ty = &field.ty;
122
123 let field_ty = super::common::to_hash_type(ty);
124
125 if target_ty.eq(&field_ty) {
126 into_token_stream.extend(quote!( self.#field_name ));
127 } else {
128 into_types.push(ty);
129
130 into_token_stream
131 .extend(quote!( ::core::convert::Into::into(self.#field_name) ));
132 }
133 }
134
135 let ident = &ast.ident;
136
137 let bound = bound.into_where_predicates_by_generic_parameters_check_types(
138 &ast.generics.params,
139 &syn::parse2(quote!(::core::convert::Into<#target_ty>)).unwrap(),
140 &into_types,
141 &[],
142 );
143
144 let mut generics = ast.generics.clone();
146
147 let where_clause = generics.make_where_clause();
148
149 for where_predicate in bound {
150 where_clause.predicates.push(where_predicate);
151 }
152
153 let (impl_generics, ty_generics, _) = ast.generics.split_for_impl();
154
155 token_stream.extend(quote! {
156 impl #impl_generics ::core::convert::Into<#target_ty> for #ident #ty_generics #where_clause {
157 #[inline]
158 fn into(self) -> #target_ty {
159 #into_token_stream
160 }
161 }
162 });
163 }
164 }
165
166 Ok(())
167 }
168}