• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015-2018 Benjamin Fry <benjaminfry@me.com>
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7 
8 //! # enum-as-inner
9 //!
10 //! A deriving proc-macro for generating functions to automatically give access to the inner members of enum.
11 //!
12 //! ## Basic unnamed field case
13 //!
14 //! The basic case is meant for single item enums, like:
15 //!
16 //! ```rust
17 //! use enum_as_inner::EnumAsInner;
18 //!
19 //! #[derive(Debug, EnumAsInner)]
20 //! enum OneEnum {
21 //!     One(u32),
22 //! }
23 //!
24 //! let one = OneEnum::One(1);
25 //!
26 //! assert_eq!(*one.as_one().unwrap(), 1);
27 //! assert_eq!(one.into_one().unwrap(), 1);
28 //! ```
29 //!
30 //! where the result is either a reference for inner items or a tuple containing the inner items.
31 //!
32 //! ## Unit case
33 //!
34 //! This will return true if enum's variant matches the expected type
35 //!
36 //! ```rust
37 //! use enum_as_inner::EnumAsInner;
38 //!
39 //! #[derive(EnumAsInner)]
40 //! enum UnitVariants {
41 //!     Zero,
42 //!     One,
43 //!     Two,
44 //! }
45 //!
46 //! let unit = UnitVariants::Two;
47 //!
48 //! assert!(unit.is_two());
49 //! ```
50 //!
51 //! ## Mutliple, unnamed field case
52 //!
53 //! This will return a tuple of the inner types:
54 //!
55 //! ```rust
56 //! use enum_as_inner::EnumAsInner;
57 //!
58 //! #[derive(Debug, EnumAsInner)]
59 //! enum ManyVariants {
60 //!     One(u32),
61 //!     Two(u32, i32),
62 //!     Three(bool, u32, i64),
63 //! }
64 //!
65 //! let many = ManyVariants::Three(true, 1, 2);
66 //!
67 //! assert!(many.is_three());
68 //! assert_eq!(many.as_three().unwrap(), (&true, &1_u32, &2_i64));
69 //! assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64));
70 //! ```
71 //!
72 //! ## Multiple, named field case
73 //!
74 //! This will return a tuple of the inner types, like the unnamed option:
75 //!
76 //! ```rust
77 //! use enum_as_inner::EnumAsInner;
78 //!
79 //! #[derive(Debug, EnumAsInner)]
80 //! enum ManyVariants {
81 //!     One { one: u32 },
82 //!     Two { one: u32, two: i32 },
83 //!     Three { one: bool, two: u32, three: i64 },
84 //! }
85 //!
86 //! let many = ManyVariants::Three { one: true, two: 1, three: 2 };
87 //!
88 //! assert!(many.is_three());
89 //! assert_eq!(many.as_three().unwrap(), (&true, &1_u32, &2_i64));
90 //! assert_eq!(many.into_three().unwrap(), (true, 1_u32, 2_i64));
91 //! ```
92 
93 #![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 
107 use heck::ToSnakeCase;
108 use proc_macro2::{Ident, Span, TokenStream};
109 use quote::quote;
110 use syn::{parse_macro_input, DeriveInput};
111 
112 /// returns first the types to return, the match names, and then tokens to the field accesses
unit_fields_return(variant_name: &syn::Ident, function_name: &Ident, doc: &str) -> TokenStream113 fn 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 
123 /// returns first the types to return, the match names, and then tokens to the field accesses
unnamed_fields_return( variant_name: &syn::Ident, (function_name_is, doc_is): (&Ident, &str), (function_name_mut_ref, doc_mut_ref): (&Ident, &str), (function_name_ref, doc_ref): (&Ident, &str), (function_name_val, doc_val): (&Ident, &str), fields: &syn::FieldsUnnamed, ) -> TokenStream124 fn 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 
212 /// returns first the types to return, the match names, and then tokens to the field accesses
named_fields_return( variant_name: &syn::Ident, (function_name_is, doc_is): (&Ident, &str), (function_name_mut_ref, doc_mut_ref): (&Ident, &str), (function_name_ref, doc_ref): (&Ident, &str), (function_name_val, doc_val): (&Ident, &str), fields: &syn::FieldsNamed, ) -> TokenStream213 fn 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 
impl_all_as_fns(ast: &DeriveInput) -> TokenStream303 fn 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 /// Derive functions on an Enum for easily accessing individual items in the Enum
388 #[proc_macro_derive(EnumAsInner)]
enum_as_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream389 pub fn enum_as_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
390     // get a usable token stream
391     let ast: DeriveInput = parse_macro_input!(input as DeriveInput);
392 
393     // Build the impl
394     let expanded: TokenStream = impl_all_as_fns(&ast);
395 
396     // Return the generated impl
397     proc_macro::TokenStream::from(expanded)
398 }
399