educe/trait_handlers/deref/
deref_struct.rs

1use 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}