1 use proc_macro2::TokenStream;
2 use quote::quote;
3 use syn::{parse_quote, Data, DeriveInput};
4
5 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
6
get_arms(ast: &DeriveInput) -> syn::Result<Vec<TokenStream>>7 fn get_arms(ast: &DeriveInput) -> syn::Result<Vec<TokenStream>> {
8 let name = &ast.ident;
9 let mut arms = Vec::new();
10 let variants = match &ast.data {
11 Data::Enum(v) => &v.variants,
12 _ => return Err(non_enum_error()),
13 };
14
15 let type_properties = ast.get_type_properties()?;
16
17 for variant in variants {
18 use syn::Fields::*;
19 let ident = &variant.ident;
20 let variant_properties = variant.get_variant_properties()?;
21
22 if variant_properties.disabled.is_some() {
23 continue;
24 }
25
26 // Look at all the serialize attributes.
27 // Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
28 // (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
29 let output = variant_properties.get_preferred_name(type_properties.case_style);
30 let params = match variant.fields {
31 Unit => quote! {},
32 Unnamed(..) => quote! { (..) },
33 Named(..) => quote! { {..} },
34 };
35
36 arms.push(quote! { #name::#ident #params => #output });
37 }
38
39 if arms.len() < variants.len() {
40 arms.push(quote! {
41 _ => panic!(
42 "AsRef::<str>::as_ref() or AsStaticRef::<str>::as_static() \
43 called on disabled variant.",
44 )
45 });
46 }
47
48 Ok(arms)
49 }
50
as_ref_str_inner(ast: &DeriveInput) -> syn::Result<TokenStream>51 pub fn as_ref_str_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
52 let name = &ast.ident;
53 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
54 let arms = get_arms(ast)?;
55 Ok(quote! {
56 impl #impl_generics ::core::convert::AsRef<str> for #name #ty_generics #where_clause {
57 fn as_ref(&self) -> &str {
58 match *self {
59 #(#arms),*
60 }
61 }
62 }
63 })
64 }
65
66 pub enum GenerateTraitVariant {
67 AsStaticStr,
68 From,
69 }
70
as_static_str_inner( ast: &DeriveInput, trait_variant: GenerateTraitVariant, ) -> syn::Result<TokenStream>71 pub fn as_static_str_inner(
72 ast: &DeriveInput,
73 trait_variant: GenerateTraitVariant,
74 ) -> syn::Result<TokenStream> {
75 let name = &ast.ident;
76 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
77 let arms = get_arms(ast)?;
78
79 let mut generics = ast.generics.clone();
80 generics
81 .params
82 .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
83 parse_quote!('_derivative_strum),
84 )));
85 let (impl_generics2, _, _) = generics.split_for_impl();
86 let arms2 = arms.clone();
87 let arms3 = arms.clone();
88
89 Ok(match trait_variant {
90 GenerateTraitVariant::AsStaticStr => quote! {
91 impl #impl_generics ::strum::AsStaticRef<str> for #name #ty_generics #where_clause {
92 fn as_static(&self) -> &'static str {
93 match *self {
94 #(#arms),*
95 }
96 }
97 }
98 },
99 GenerateTraitVariant::From => quote! {
100 impl #impl_generics ::core::convert::From<#name #ty_generics> for &'static str #where_clause {
101 fn from(x: #name #ty_generics) -> &'static str {
102 match x {
103 #(#arms2),*
104 }
105 }
106 }
107 impl #impl_generics2 ::core::convert::From<&'_derivative_strum #name #ty_generics> for &'static str #where_clause {
108 fn from(x: &'_derivative_strum #name #ty_generics) -> &'static str {
109 match *x {
110 #(#arms3),*
111 }
112 }
113 }
114 },
115 })
116 }
117