1 | use proc_macro2::TokenStream; |
2 | use quote::quote; |
3 | use syn::{Data, DeriveInput, Fields, LitStr}; |
4 | |
5 | use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; |
6 | |
7 | pub fn enum_message_inner(ast: &DeriveInput) -> syn::Result<TokenStream> { |
8 | let name = &ast.ident; |
9 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); |
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 | let strum_module_path = type_properties.crate_module_path(); |
17 | |
18 | let mut arms = Vec::new(); |
19 | let mut detailed_arms = Vec::new(); |
20 | let mut documentation_arms = Vec::new(); |
21 | let mut serializations = Vec::new(); |
22 | |
23 | for variant in variants { |
24 | let variant_properties = variant.get_variant_properties()?; |
25 | let messages = variant_properties.message.as_ref(); |
26 | let detailed_messages = variant_properties.detailed_message.as_ref(); |
27 | let documentation = &variant_properties.documentation; |
28 | let ident = &variant.ident; |
29 | |
30 | let params = match variant.fields { |
31 | Fields::Unit => quote! {}, |
32 | Fields::Unnamed(..) => quote! { (..) }, |
33 | Fields::Named(..) => quote! { {..} }, |
34 | }; |
35 | |
36 | // You can't disable getting the serializations. |
37 | { |
38 | let serialization_variants = |
39 | variant_properties.get_serializations(type_properties.case_style); |
40 | |
41 | let count = serialization_variants.len(); |
42 | serializations.push(quote! { |
43 | &#name::#ident #params => { |
44 | static ARR: [&'static str; #count] = [#(#serialization_variants),*]; |
45 | &ARR |
46 | } |
47 | }); |
48 | } |
49 | |
50 | // But you can disable the messages. |
51 | if variant_properties.disabled.is_some() { |
52 | continue; |
53 | } |
54 | |
55 | if let Some(msg) = messages { |
56 | let params = params.clone(); |
57 | |
58 | // Push the simple message. |
59 | let tokens = quote! { &#name::#ident #params => ::core::option::Option::Some(#msg) }; |
60 | arms.push(tokens.clone()); |
61 | |
62 | if detailed_messages.is_none() { |
63 | detailed_arms.push(tokens); |
64 | } |
65 | } |
66 | |
67 | if let Some(msg) = detailed_messages { |
68 | let params = params.clone(); |
69 | // Push the detailed message. |
70 | detailed_arms |
71 | .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#msg) }); |
72 | } |
73 | |
74 | if !documentation.is_empty() { |
75 | let params = params.clone(); |
76 | // Strip a single leading space from each documentation line. |
77 | let documentation: Vec<LitStr> = documentation |
78 | .iter() |
79 | .map(|lit_str| { |
80 | let line = lit_str.value(); |
81 | if line.starts_with(' ' ) { |
82 | LitStr::new(&line.as_str()[1..], lit_str.span()) |
83 | } else { |
84 | lit_str.clone() |
85 | } |
86 | }) |
87 | .collect(); |
88 | if documentation.len() == 1 { |
89 | let text = &documentation[0]; |
90 | documentation_arms |
91 | .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#text) }); |
92 | } else { |
93 | // Push the documentation. |
94 | documentation_arms |
95 | .push(quote! { |
96 | &#name::#ident #params => ::core::option::Option::Some(concat!(#(concat!(#documentation, " \n" )),*)) |
97 | }); |
98 | } |
99 | } |
100 | } |
101 | |
102 | if arms.len() < variants.len() { |
103 | arms.push(quote! { _ => ::core::option::Option::None }); |
104 | } |
105 | |
106 | if detailed_arms.len() < variants.len() { |
107 | detailed_arms.push(quote! { _ => ::core::option::Option::None }); |
108 | } |
109 | |
110 | if documentation_arms.len() < variants.len() { |
111 | documentation_arms.push(quote! { _ => ::core::option::Option::None }); |
112 | } |
113 | |
114 | Ok(quote! { |
115 | impl #impl_generics #strum_module_path::EnumMessage for #name #ty_generics #where_clause { |
116 | fn get_message(&self) -> ::core::option::Option<&'static str> { |
117 | match self { |
118 | #(#arms),* |
119 | } |
120 | } |
121 | |
122 | fn get_detailed_message(&self) -> ::core::option::Option<&'static str> { |
123 | match self { |
124 | #(#detailed_arms),* |
125 | } |
126 | } |
127 | |
128 | fn get_documentation(&self) -> ::core::option::Option<&'static str> { |
129 | match self { |
130 | #(#documentation_arms),* |
131 | } |
132 | } |
133 | |
134 | fn get_serializations(&self) -> &'static [&'static str] { |
135 | match self { |
136 | #(#serializations),* |
137 | } |
138 | } |
139 | } |
140 | }) |
141 | } |
142 | |