educe/trait_handlers/deref/
deref_struct.rs1use quote::quote;
2use syn::{spanned::Spanned, Data, DeriveInput, Field, Meta};
3
4use super::{
5 models::{FieldAttributeBuilder, TypeAttributeBuilder},
6 TraitHandler,
7};
8use crate::{
9 common::{ident_index::IdentOrIndex, r#type::dereference_changed},
10 Trait,
11};
12
13pub(crate) struct DerefStructHandler;
14
15impl TraitHandler for DerefStructHandler {
16 #[inline]
17 fn trait_meta_handler(
18 ast: &DeriveInput,
19 token_stream: &mut proc_macro2::TokenStream,
20 traits: &[Trait],
21 meta: &Meta,
22 ) -> syn::Result<()> {
23 let _ = TypeAttributeBuilder {
24 enable_flag: true
25 }
26 .build_from_deref_meta(meta)?;
27
28 let mut target_token_stream = proc_macro2::TokenStream::new();
29 let mut deref_token_stream = proc_macro2::TokenStream::new();
30
31 if let Data::Struct(data) = &ast.data {
32 let (index, field) = {
33 let fields = &data.fields;
34
35 if fields.len() == 1 {
36 let field = fields.into_iter().next().unwrap();
37
38 let _ = FieldAttributeBuilder {
39 enable_flag: true
40 }
41 .build_from_attributes(&field.attrs, traits)?;
42
43 (0usize, field)
44 } else {
45 let mut deref_field: Option<(usize, &Field)> = None;
46
47 for (index, field) in fields.iter().enumerate() {
48 let field_attribute = FieldAttributeBuilder {
49 enable_flag: true
50 }
51 .build_from_attributes(&field.attrs, traits)?;
52
53 if field_attribute.flag {
54 if deref_field.is_some() {
55 return Err(super::panic::multiple_deref_fields(
56 field_attribute.span,
57 ));
58 }
59
60 deref_field = Some((index, field));
61 }
62 }
63
64 if let Some(deref_field) = deref_field {
65 deref_field
66 } else {
67 return Err(super::panic::no_deref_field(meta.span()));
68 }
69 }
70 };
71
72 let ty = &field.ty;
73 let (dereference_ty, is_ref) = dereference_changed(ty);
74
75 target_token_stream.extend(quote!(#dereference_ty));
76
77 let field_name = IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
78
79 deref_token_stream.extend(if is_ref {
80 quote! (self.#field_name)
81 } else {
82 quote! (&self.#field_name)
83 });
84 }
85
86 let ident = &ast.ident;
87
88 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
89
90 token_stream.extend(quote! {
91 impl #impl_generics ::core::ops::Deref for #ident #ty_generics #where_clause {
92 type Target = #target_token_stream;
93
94 #[inline]
95 fn deref(&self) -> &Self::Target {
96 #deref_token_stream
97 }
98 }
99 });
100
101 Ok(())
102 }
103}