1 use proc_macro2::TokenStream;
2 use syn;
3 
4 use crate::helpers::case_style::CaseStyle;
5 use helpers::{extract_meta, CaseStyleHelpers, MetaIteratorHelpers};
6 
get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream>7 fn get_arms(ast: &syn::DeriveInput) -> Vec<TokenStream> {
8     let name = &ast.ident;
9     let mut arms = Vec::new();
10     let variants = match ast.data {
11         syn::Data::Enum(ref v) => &v.variants,
12         _ => panic!("This macro only works on Enums"),
13     };
14 
15     let type_meta = extract_meta(&ast.attrs);
16     let case_style = type_meta
17         .find_unique_property("strum", "serialize_all")
18         .map(|style| CaseStyle::from(style.as_ref()));
19 
20     for variant in variants {
21         use syn::Fields::*;
22         let ident = &variant.ident;
23         let meta = extract_meta(&variant.attrs);
24 
25         if meta.is_disabled() {
26             continue;
27         }
28 
29         // Look at all the serialize attributes.
30         // Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
31         // (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
32         let output = if let Some(n) = meta.find_unique_property("strum", "to_string") {
33             n
34         } else {
35             let mut attrs = meta.find_properties("strum", "serialize");
36             // We always take the longest one. This is arbitary, but is *mostly* deterministic
37             attrs.sort_by_key(|s| s.len());
38             if let Some(n) = attrs.pop() {
39                 n
40             } else {
41                 ident.convert_case(case_style)
42             }
43         };
44 
45         let params = match variant.fields {
46             Unit => quote! {},
47             Unnamed(..) => quote! { (..) },
48             Named(..) => quote! { {..} },
49         };
50 
51         arms.push(quote! { #name::#ident #params => #output });
52     }
53 
54     if arms.len() < variants.len() {
55         arms.push(quote! {
56         _ => panic!("AsRef::<str>::as_ref() or AsStaticRef::<str>::as_static() \
57                      called on disabled variant.")
58         })
59     }
60 
61     arms
62 }
63 
as_ref_str_inner(ast: &syn::DeriveInput) -> TokenStream64 pub fn as_ref_str_inner(ast: &syn::DeriveInput) -> TokenStream {
65     let name = &ast.ident;
66     let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
67     let arms = get_arms(ast);
68     quote! {
69         impl #impl_generics ::std::convert::AsRef<str> for #name #ty_generics #where_clause {
70             fn as_ref(&self) -> &str {
71                 match *self {
72                     #(#arms),*
73                 }
74             }
75         }
76     }
77 }
78 
79 pub enum GenerateTraitVariant {
80     AsStaticStr,
81     From,
82 }
83 
as_static_str_inner( ast: &syn::DeriveInput, trait_variant: GenerateTraitVariant, ) -> TokenStream84 pub fn as_static_str_inner(
85     ast: &syn::DeriveInput,
86     trait_variant: GenerateTraitVariant,
87 ) -> TokenStream {
88     let name = &ast.ident;
89     let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
90     let arms = get_arms(ast);
91 
92     let mut generics = ast.generics.clone();
93     generics
94         .params
95         .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
96             parse_quote!('_derivative_strum),
97         )));
98     let (impl_generics2, _, _) = generics.split_for_impl();
99     let arms2 = arms.clone();
100     let arms3 = arms.clone();
101     match trait_variant {
102         GenerateTraitVariant::AsStaticStr => {
103             quote! {
104                 impl #impl_generics ::strum::AsStaticRef<str> for #name #ty_generics #where_clause {
105                     fn as_static(&self) -> &'static str {
106                         match *self {
107                             #(#arms),*
108                         }
109                     }
110                 }
111             }
112         }
113         GenerateTraitVariant::From => {
114             quote! {
115             impl #impl_generics ::std::convert::From<#name #ty_generics> for &'static str #where_clause {
116                 fn from(x: #name #ty_generics) -> &'static str {
117                     match x {
118                         #(#arms2),*
119                     }
120                 }
121             }
122             impl #impl_generics2 ::std::convert::From<&'_derivative_strum #name #ty_generics> for &'static str #where_clause {
123                 fn from(x: &'_derivative_strum #name #ty_generics) -> &'static str {
124                     match *x {
125                         #(#arms3),*
126                     }
127                 }
128             }
129             }
130         }
131     }
132 }
133