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.iter().map(|lit_str| { |
78 | let line = lit_str.value(); |
79 | if line.starts_with(' ' ) { |
80 | LitStr::new(&line.as_str()[1..], lit_str.span()) |
81 | } else { |
82 | lit_str.clone() |
83 | } |
84 | }).collect(); |
85 | if documentation.len() == 1 { |
86 | let text = &documentation[0]; |
87 | documentation_arms |
88 | .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#text) }); |
89 | } else { |
90 | // Push the documentation. |
91 | documentation_arms |
92 | .push(quote! { |
93 | &#name::#ident #params => ::core::option::Option::Some(concat!(#(concat!(#documentation, " \n" )),*)) |
94 | }); |
95 | } |
96 | } |
97 | } |
98 | |
99 | if arms.len() < variants.len() { |
100 | arms.push(quote! { _ => ::core::option::Option::None }); |
101 | } |
102 | |
103 | if detailed_arms.len() < variants.len() { |
104 | detailed_arms.push(quote! { _ => ::core::option::Option::None }); |
105 | } |
106 | |
107 | if documentation_arms.len() < variants.len() { |
108 | documentation_arms.push(quote! { _ => ::core::option::Option::None }); |
109 | } |
110 | |
111 | Ok(quote! { |
112 | impl #impl_generics #strum_module_path::EnumMessage for #name #ty_generics #where_clause { |
113 | fn get_message(&self) -> ::core::option::Option<&'static str> { |
114 | match self { |
115 | #(#arms),* |
116 | } |
117 | } |
118 | |
119 | fn get_detailed_message(&self) -> ::core::option::Option<&'static str> { |
120 | match self { |
121 | #(#detailed_arms),* |
122 | } |
123 | } |
124 | |
125 | fn get_documentation(&self) -> ::core::option::Option<&'static str> { |
126 | match self { |
127 | #(#documentation_arms),* |
128 | } |
129 | } |
130 | |
131 | fn get_serializations(&self) -> &'static [&'static str] { |
132 | match self { |
133 | #(#serializations),* |
134 | } |
135 | } |
136 | } |
137 | }) |
138 | } |
139 | |