educe/trait_handlers/deref_mut/
deref_mut_enum.rs1use std::{fmt::Write, str::FromStr};
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{Data, DeriveInput, Fields, Meta};
6
7use super::{
8 super::TraitHandler,
9 models::{FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::{panic, Trait};
12
13pub struct DerefMutEnumHandler;
14
15impl TraitHandler for DerefMutEnumHandler {
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_mut_meta(meta);
26
27 let enum_name = ast.ident.to_string();
28
29 let mut deref_mut_tokens = TokenStream::new();
30
31 let mut match_tokens = String::from("match self {");
32
33 if let Data::Enum(data) = &ast.data {
34 for variant in data.variants.iter() {
35 let _ = TypeAttributeBuilder {
36 enable_flag: false
37 }
38 .from_attributes(&variant.attrs, traits);
39
40 let variant_ident = variant.ident.to_string();
41
42 match &variant.fields {
43 Fields::Unit => {
44 panic::deref_mut_cannot_support_unit_variant();
46 },
47 Fields::Named(fields) => {
48 let mut pattern_tokens = String::new();
50 let mut block_tokens = String::new();
51
52 let mut counter = 0;
53
54 for field in fields.named.iter() {
55 let field_attribute = FieldAttributeBuilder {
56 enable_flag: true
57 }
58 .from_attributes(&field.attrs, traits);
59
60 if field_attribute.flag {
61 if !block_tokens.is_empty() {
62 panic::multiple_deref_mut_fields_of_variant(&variant_ident);
63 }
64
65 let field_name = field.ident.as_ref().unwrap().to_string();
66
67 block_tokens
68 .write_fmt(format_args!(
69 "return {field_name};",
70 field_name = field_name
71 ))
72 .unwrap();
73 pattern_tokens
74 .write_fmt(format_args!(
75 "{field_name}, ..",
76 field_name = field_name
77 ))
78 .unwrap();
79 }
80
81 counter += 1;
82 }
83
84 if block_tokens.is_empty() {
85 if counter == 1 {
86 let field = fields.named.iter().next().unwrap();
87
88 let field_name = field.ident.as_ref().unwrap().to_string();
89
90 block_tokens
91 .write_fmt(format_args!(
92 "return {field_name};",
93 field_name = field_name
94 ))
95 .unwrap();
96 pattern_tokens
97 .write_fmt(format_args!(
98 "{field_name}, ..",
99 field_name = field_name
100 ))
101 .unwrap();
102 } else {
103 panic::no_deref_mut_field_of_variant(&variant_ident);
104 }
105 }
106
107 match_tokens
108 .write_fmt(format_args!(
109 "{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ \
110 {block_tokens} }}",
111 enum_name = enum_name,
112 variant_ident = variant_ident,
113 pattern_tokens = pattern_tokens,
114 block_tokens = block_tokens
115 ))
116 .unwrap();
117 },
118 Fields::Unnamed(fields) => {
119 let mut pattern_tokens = String::new();
121 let mut block_tokens = String::new();
122
123 let mut counter = 0;
124
125 for (index, field) in fields.unnamed.iter().enumerate() {
126 let field_attribute = FieldAttributeBuilder {
127 enable_flag: true
128 }
129 .from_attributes(&field.attrs, traits);
130
131 if field_attribute.flag {
132 if !block_tokens.is_empty() {
133 panic::multiple_deref_mut_fields_of_variant(&variant_ident);
134 }
135
136 let field_name = format!("{}", index);
137
138 block_tokens
139 .write_fmt(format_args!(
140 "return _{field_name};",
141 field_name = field_name
142 ))
143 .unwrap();
144 pattern_tokens
145 .write_fmt(format_args!(
146 "_{field_name},",
147 field_name = field_name
148 ))
149 .unwrap();
150 } else {
151 pattern_tokens.push_str("_,");
152 }
153
154 counter += 1;
155 }
156
157 if block_tokens.is_empty() {
158 if counter == 1 {
159 let field_name = String::from("0");
160
161 block_tokens
162 .write_fmt(format_args!(
163 "return _{field_name};",
164 field_name = field_name
165 ))
166 .unwrap();
167
168 pattern_tokens.clear();
169 pattern_tokens
170 .write_fmt(format_args!(
171 "_{field_name}",
172 field_name = field_name
173 ))
174 .unwrap();
175 } else {
176 panic::no_deref_mut_field_of_variant(&variant_ident);
177 }
178 }
179
180 match_tokens
181 .write_fmt(format_args!(
182 "{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ \
183 {block_tokens} }}",
184 enum_name = enum_name,
185 variant_ident = variant_ident,
186 pattern_tokens = pattern_tokens,
187 block_tokens = block_tokens
188 ))
189 .unwrap();
190 },
191 }
192 }
193 }
194
195 match_tokens.push('}');
196
197 deref_mut_tokens.extend(TokenStream::from_str(&match_tokens).unwrap());
198
199 let ident = &ast.ident;
200
201 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
202
203 let deref_mut_impl = quote! {
204 impl #impl_generics core::ops::DerefMut for #ident #ty_generics #where_clause {
205 #[inline]
206 fn deref_mut(&mut self) -> &mut Self::Target {
207 #deref_mut_tokens
208 }
209 }
210 };
211
212 tokens.extend(deref_mut_impl);
213 }
214}