educe/trait_handlers/deref/
deref_struct.rs1use std::str::FromStr;
2
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{Data, DeriveInput, Meta};
6
7use super::{
8 super::TraitHandler,
9 models::{FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::{panic, Trait};
12
13pub struct DerefStructHandler;
14
15impl TraitHandler for DerefStructHandler {
16 fn trait_meta_handler(
17 ast: &DeriveInput,
18 tokens: &mut TokenStream,
19 traits: &[Trait],
20 meta: &Meta,
21 ) {
22 let _ = TypeAttributeBuilder {
23 enable_flag: true
24 }
25 .from_deref_meta(meta);
26
27 let mut ty = TokenStream::new();
28 let mut deref_tokens = TokenStream::new();
29
30 if let Data::Struct(data) = &ast.data {
31 let mut counter = 0;
32
33 for (index, field) in data.fields.iter().enumerate() {
34 let field_attribute = FieldAttributeBuilder {
35 enable_flag: true
36 }
37 .from_attributes(&field.attrs, traits);
38
39 if field_attribute.flag {
40 if !ty.is_empty() {
41 panic::multiple_deref_fields();
42 }
43
44 let field_name = if let Some(ident) = field.ident.as_ref() {
45 ident.to_string()
46 } else {
47 format!("{}", index)
48 };
49
50 ty.extend(field.ty.clone().into_token_stream());
51 deref_tokens.extend(
52 TokenStream::from_str(&format!(
53 "&self.{field_name}",
54 field_name = field_name
55 ))
56 .unwrap(),
57 );
58 }
59
60 counter += 1;
61 }
62
63 if ty.is_empty() {
64 if counter == 1 {
65 let field = data.fields.iter().next().unwrap();
66
67 let field_name = if let Some(ident) = field.ident.as_ref() {
68 ident.to_string()
69 } else {
70 String::from("0")
71 };
72
73 ty.extend(field.ty.clone().into_token_stream());
74 deref_tokens.extend(
75 TokenStream::from_str(&format!(
76 "&self.{field_name}",
77 field_name = field_name
78 ))
79 .unwrap(),
80 );
81 } else {
82 panic::no_deref_field();
83 }
84 }
85 }
86
87 let ident = &ast.ident;
88
89 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
90
91 let deref_impl = quote! {
92 impl #impl_generics core::ops::Deref for #ident #ty_generics #where_clause {
93 type Target = #ty;
94
95 #[inline]
96 fn deref(&self) -> &Self::Target {
97 #deref_tokens
98 }
99 }
100 };
101
102 tokens.extend(deref_impl);
103 }
104}