1#![warn(
94 clippy::default_trait_access,
95 clippy::dbg_macro,
96 clippy::print_stdout,
97 clippy::unimplemented,
98 clippy::use_self,
99 missing_copy_implementations,
100 missing_docs,
101 non_snake_case,
102 non_upper_case_globals,
103 rust_2018_idioms,
104 unreachable_pub
105)]
106
107use heck::ToSnakeCase;
108use proc_macro2::{Ident, Span, TokenStream};
109use quote::quote;
110use syn::{parse_macro_input, DeriveInput};
111
112fn unit_fields_return(variant_name: &syn::Ident, function_name: &Ident, doc: &str) -> TokenStream {
114 quote!(
115 #[doc = #doc]
116 #[inline]
117 pub fn #function_name(&self) -> bool {
118 matches!(self, Self::#variant_name)
119 }
120 )
121}
122
123fn unnamed_fields_return(
125 variant_name: &syn::Ident,
126 (function_name_is, doc_is): (&Ident, &str),
127 (function_name_mut_ref, doc_mut_ref): (&Ident, &str),
128 (function_name_ref, doc_ref): (&Ident, &str),
129 (function_name_val, doc_val): (&Ident, &str),
130 fields: &syn::FieldsUnnamed,
131) -> TokenStream {
132 let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.unnamed.len() {
133 1 => {
134 let field = fields.unnamed.first().expect("no fields on type");
135
136 let returns = &field.ty;
137 let returns_mut_ref = quote!(&mut #returns);
138 let returns_ref = quote!(&#returns);
139 let returns_val = quote!(#returns);
140 let matches = quote!(inner);
141
142 (returns_mut_ref, returns_ref, returns_val, matches)
143 }
144 0 => (quote!(()), quote!(()), quote!(()), quote!()),
145 _ => {
146 let mut returns_mut_ref = TokenStream::new();
147 let mut returns_ref = TokenStream::new();
148 let mut returns_val = TokenStream::new();
149 let mut matches = TokenStream::new();
150
151 for (i, field) in fields.unnamed.iter().enumerate() {
152 let rt = &field.ty;
153 let match_name = Ident::new(&format!("match_{}", i), Span::call_site());
154 returns_mut_ref.extend(quote!(&mut #rt,));
155 returns_ref.extend(quote!(&#rt,));
156 returns_val.extend(quote!(#rt,));
157 matches.extend(quote!(#match_name,));
158 }
159
160 (
161 quote!((#returns_mut_ref)),
162 quote!((#returns_ref)),
163 quote!((#returns_val)),
164 quote!(#matches),
165 )
166 }
167 };
168
169 quote!(
170 #[doc = #doc_is ]
171 #[inline]
172 #[allow(unused_variables)]
173 pub fn #function_name_is(&self) -> bool {
174 matches!(self, Self::#variant_name(#matches))
175 }
176
177 #[doc = #doc_mut_ref ]
178 #[inline]
179 pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> {
180 match self {
181 Self::#variant_name(#matches) => {
182 ::core::option::Option::Some((#matches))
183 }
184 _ => ::core::option::Option::None
185 }
186 }
187
188 #[doc = #doc_ref ]
189 #[inline]
190 pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> {
191 match self {
192 Self::#variant_name(#matches) => {
193 ::core::option::Option::Some((#matches))
194 }
195 _ => ::core::option::Option::None
196 }
197 }
198
199 #[doc = #doc_val ]
200 #[inline]
201 pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> {
202 match self {
203 Self::#variant_name(#matches) => {
204 ::core::result::Result::Ok((#matches))
205 },
206 _ => ::core::result::Result::Err(self)
207 }
208 }
209 )
210}
211
212fn named_fields_return(
214 variant_name: &syn::Ident,
215 (function_name_is, doc_is): (&Ident, &str),
216 (function_name_mut_ref, doc_mut_ref): (&Ident, &str),
217 (function_name_ref, doc_ref): (&Ident, &str),
218 (function_name_val, doc_val): (&Ident, &str),
219 fields: &syn::FieldsNamed,
220) -> TokenStream {
221 let (returns_mut_ref, returns_ref, returns_val, matches) = match fields.named.len() {
222 1 => {
223 let field = fields.named.first().expect("no fields on type");
224 let match_name = field.ident.as_ref().expect("expected a named field");
225
226 let returns = &field.ty;
227 let returns_mut_ref = quote!(&mut #returns);
228 let returns_ref = quote!(&#returns);
229 let returns_val = quote!(#returns);
230 let matches = quote!(#match_name);
231
232 (returns_mut_ref, returns_ref, returns_val, matches)
233 }
234 0 => (quote!(()), quote!(()), quote!(()), quote!(())),
235 _ => {
236 let mut returns_mut_ref = TokenStream::new();
237 let mut returns_ref = TokenStream::new();
238 let mut returns_val = TokenStream::new();
239 let mut matches = TokenStream::new();
240
241 for field in fields.named.iter() {
242 let rt = &field.ty;
243 let match_name = field.ident.as_ref().expect("expected a named field");
244
245 returns_mut_ref.extend(quote!(&mut #rt,));
246 returns_ref.extend(quote!(&#rt,));
247 returns_val.extend(quote!(#rt,));
248 matches.extend(quote!(#match_name,));
249 }
250
251 (
252 quote!((#returns_mut_ref)),
253 quote!((#returns_ref)),
254 quote!((#returns_val)),
255 quote!(#matches),
256 )
257 }
258 };
259
260 quote!(
261 #[doc = #doc_is ]
262 #[inline]
263 #[allow(unused_variables)]
264 pub fn #function_name_is(&self) -> bool {
265 matches!(self, Self::#variant_name{ #matches })
266 }
267
268 #[doc = #doc_mut_ref ]
269 #[inline]
270 pub fn #function_name_mut_ref(&mut self) -> ::core::option::Option<#returns_mut_ref> {
271 match self {
272 Self::#variant_name{ #matches } => {
273 ::core::option::Option::Some((#matches))
274 }
275 _ => ::core::option::Option::None
276 }
277 }
278
279 #[doc = #doc_ref ]
280 #[inline]
281 pub fn #function_name_ref(&self) -> ::core::option::Option<#returns_ref> {
282 match self {
283 Self::#variant_name{ #matches } => {
284 ::core::option::Option::Some((#matches))
285 }
286 _ => ::core::option::Option::None
287 }
288 }
289
290 #[doc = #doc_val ]
291 #[inline]
292 pub fn #function_name_val(self) -> ::core::result::Result<#returns_val, Self> {
293 match self {
294 Self::#variant_name{ #matches } => {
295 ::core::result::Result::Ok((#matches))
296 }
297 _ => ::core::result::Result::Err(self)
298 }
299 }
300 )
301}
302
303fn impl_all_as_fns(ast: &DeriveInput) -> TokenStream {
304 let name = &ast.ident;
305 let generics = &ast.generics;
306
307 let enum_data = if let syn::Data::Enum(data) = &ast.data {
308 data
309 } else {
310 panic!("{} is not an enum", name);
311 };
312
313 let mut stream = TokenStream::new();
314
315 for variant_data in &enum_data.variants {
316 let variant_name = &variant_data.ident;
317 let function_name_ref = Ident::new(
318 &format!("as_{}", variant_name).to_snake_case(),
319 Span::call_site(),
320 );
321 let doc_ref = format!(
322 "Optionally returns references to the inner fields if this is a `{}::{}`, otherwise `None`",
323 name,
324 variant_name,
325 );
326 let function_name_mut_ref = Ident::new(
327 &format!("as_{}_mut", variant_name).to_snake_case(),
328 Span::call_site(),
329 );
330 let doc_mut_ref = format!(
331 "Optionally returns mutable references to the inner fields if this is a `{}::{}`, otherwise `None`",
332 name,
333 variant_name,
334 );
335
336 let function_name_val = Ident::new(
337 &format!("into_{}", variant_name).to_snake_case(),
338 Span::call_site(),
339 );
340 let doc_val = format!(
341 "Returns the inner fields if this is a `{}::{}`, otherwise returns back the enum in the `Err` case of the result",
342 name,
343 variant_name,
344 );
345
346 let function_name_is = Ident::new(
347 &format!("is_{}", variant_name).to_snake_case(),
348 Span::call_site(),
349 );
350 let doc_is = format!(
351 "Returns true if this is a `{}::{}`, otherwise false",
352 name, variant_name,
353 );
354
355 let tokens = match &variant_data.fields {
356 syn::Fields::Unit => unit_fields_return(variant_name, &function_name_is, &doc_is),
357 syn::Fields::Unnamed(unnamed) => unnamed_fields_return(
358 variant_name,
359 (&function_name_is, &doc_is),
360 (&function_name_mut_ref, &doc_mut_ref),
361 (&function_name_ref, &doc_ref),
362 (&function_name_val, &doc_val),
363 unnamed,
364 ),
365 syn::Fields::Named(named) => named_fields_return(
366 variant_name,
367 (&function_name_is, &doc_is),
368 (&function_name_mut_ref, &doc_mut_ref),
369 (&function_name_ref, &doc_ref),
370 (&function_name_val, &doc_val),
371 named,
372 ),
373 };
374
375 stream.extend(tokens);
376 }
377
378 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
379
380 quote!(
381 impl #impl_generics #name #ty_generics #where_clause {
382 #stream
383 }
384 )
385}
386
387#[proc_macro_derive(EnumAsInner)]
389pub fn enum_as_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
390 let ast: DeriveInput = parse_macro_input!(input as DeriveInput);
392
393 let expanded: TokenStream = impl_all_as_fns(&ast);
395
396 proc_macro::TokenStream::from(expanded)
398}