enum_group_macros_impl/
lib.rs1use proc_macro::TokenStream;
9use proc_macro2::TokenStream as TokenStream2;
10use quote::{format_ident, quote};
11use syn::parse::{Parse, ParseStream};
12use syn::{braced, parse_macro_input, Attribute, Ident, Token, Type, Visibility};
13
14#[derive(Debug)]
20struct ParsedVariant {
21 attrs: Vec<Attribute>,
22 name: Ident,
23 ty: Type,
24}
25
26#[derive(Debug)]
28struct ParsedGroup {
29 name: Ident,
30 variants: Vec<ParsedVariant>,
31}
32
33#[derive(Debug)]
35struct EnumGroupInput {
36 attrs: Vec<Attribute>,
37 vis: Visibility,
38 name: Ident,
39 groups: Vec<ParsedGroup>,
40}
41
42impl Parse for ParsedVariant {
43 fn parse(input: ParseStream) -> syn::Result<Self> {
44 let attrs = input.call(Attribute::parse_outer)?;
45 let name: Ident = input.parse()?;
46
47 let content;
49 syn::parenthesized!(content in input);
50 let ty: Type = content.parse()?;
51
52 Ok(ParsedVariant { attrs, name, ty })
53 }
54}
55
56impl Parse for ParsedGroup {
57 fn parse(input: ParseStream) -> syn::Result<Self> {
58 let name: Ident = input.parse()?;
59
60 let content;
61 braced!(content in input);
62
63 let mut variants = Vec::new();
64 while !content.is_empty() {
65 variants.push(content.parse::<ParsedVariant>()?);
66 if content.peek(Token![,]) {
68 content.parse::<Token![,]>()?;
69 }
70 }
71
72 Ok(ParsedGroup { name, variants })
73 }
74}
75
76impl Parse for EnumGroupInput {
77 fn parse(input: ParseStream) -> syn::Result<Self> {
78 let attrs = input.call(Attribute::parse_outer)?;
80
81 let vis: Visibility = input.parse()?;
83 input.parse::<Token![enum]>()?;
84 let name: Ident = input.parse()?;
85
86 let content;
88 braced!(content in input);
89
90 let mut groups = Vec::new();
91 while !content.is_empty() {
92 groups.push(content.parse::<ParsedGroup>()?);
93 if content.peek(Token![,]) {
95 content.parse::<Token![,]>()?;
96 }
97 }
98
99 Ok(EnumGroupInput { attrs, vis, name, groups })
100 }
101}
102
103fn generate_enum_group(input: EnumGroupInput) -> TokenStream2 {
108 let EnumGroupInput { attrs, vis, name: wire_name, groups } = input;
109
110 let group_enum_name = format_ident!("{}Group", wire_name);
111
112 let mut all_variants = Vec::new();
114 let mut group_enum_variants = Vec::new();
115 let mut into_group_arms = Vec::new();
116
117 let group_enums: Vec<TokenStream2> = groups
119 .iter()
120 .map(|group| {
121 let group_name = &group.name;
122
123 let variants: Vec<TokenStream2> = group
125 .variants
126 .iter()
127 .map(|v| {
128 let v_attrs = &v.attrs;
129 let v_name = &v.name;
130 let v_ty = &v.ty;
131 quote! {
132 #(#v_attrs)*
133 #v_name(#v_ty)
134 }
135 })
136 .collect();
137
138 for v in &group.variants {
140 let v_attrs = &v.attrs;
141 let v_name = &v.name;
142 let v_ty = &v.ty;
143 all_variants.push(quote! {
144 #(#v_attrs)*
145 #v_name(#v_ty)
146 });
147
148 into_group_arms.push(quote! {
150 Self::#v_name(v) => #group_enum_name::#group_name(#group_name::#v_name(v))
151 });
152 }
153
154 group_enum_variants.push(quote! {
156 #group_name(#group_name)
157 });
158
159 quote! {
161 #(#attrs)*
162 #vis enum #group_name {
163 #(#variants),*
164 }
165 }
166 })
167 .collect();
168
169 let wire_enum = quote! {
171 #(#attrs)*
172 #vis enum #wire_name {
173 #(#all_variants),*
174 }
175 };
176
177 let group_dispatch_enum = quote! {
179 #[derive(Debug, Clone)]
180 #vis enum #group_enum_name {
181 #(#group_enum_variants),*
182 }
183 };
184
185 let inherent_impl = quote! {
187 impl #wire_name {
188 #vis fn into_group(self) -> #group_enum_name {
190 match self {
191 #(#into_group_arms),*
192 }
193 }
194 }
195 };
196
197 let trait_impl = quote! {
199 impl ::enum_group_macros::EnumGroup for #wire_name {
200 type Group = #group_enum_name;
201
202 fn into_group(self) -> Self::Group {
203 #wire_name::into_group(self)
205 }
206 }
207 };
208
209 quote! {
211 #(#group_enums)*
212
213 #wire_enum
214
215 #group_dispatch_enum
216
217 #inherent_impl
218
219 #trait_impl
220 }
221}
222
223#[proc_macro]
263pub fn define_enum_group(input: TokenStream) -> TokenStream {
264 let input = parse_macro_input!(input as EnumGroupInput);
265 generate_enum_group(input).into()
266}
267
268#[proc_macro]
294pub fn match_enum_group(input: TokenStream) -> TokenStream {
295 let input2: TokenStream2 = input.into();
296
297 let result = parse_match_enum_group(input2);
298
299 match result {
300 Ok(tokens) => tokens.into(),
301 Err(e) => e.to_compile_error().into(),
302 }
303}
304
305struct MatchArm {
307 group_name: Ident,
308 binding: proc_macro2::TokenStream,
309 body: TokenStream2,
310}
311
312fn parse_match_enum_group(input: TokenStream2) -> syn::Result<TokenStream2> {
313 use syn::parse::Parser;
314
315 let parser = |input: ParseStream| -> syn::Result<(syn::Expr, Ident, Vec<MatchArm>)> {
316 let val: syn::Expr = input.parse()?;
318 input.parse::<Token![,]>()?;
319
320 let wire: Ident = input.parse()?;
322 input.parse::<Token![,]>()?;
323
324 let content;
326 braced!(content in input);
327
328 let mut arms = Vec::new();
329 while !content.is_empty() {
330 let group_name: Ident = content.parse()?;
332
333 let paren_content;
334 syn::parenthesized!(paren_content in content);
335 let binding: proc_macro2::TokenStream = paren_content.parse()?;
337
338 content.parse::<Token![=>]>()?;
339
340 let body: syn::Expr = content.parse()?;
342
343 arms.push(MatchArm { group_name, binding, body: quote! { #body } });
344
345 if content.peek(Token![,]) {
347 content.parse::<Token![,]>()?;
348 }
349 }
350
351 Ok((val, wire, arms))
352 };
353
354 let (val, wire, arms) = parser.parse2(input)?;
355
356 let match_arms: Vec<TokenStream2> = arms
358 .iter()
359 .map(|arm| {
360 let group_name = &arm.group_name;
361 let binding = &arm.binding;
362 let body = &arm.body;
363
364 quote! {
365 __EnumGroup__::#group_name(#binding) => #body
366 }
367 })
368 .collect();
369
370 Ok(quote! {
373 {
374 #[allow(non_camel_case_types)]
375 type __EnumGroup__ = <#wire as ::enum_group_macros::EnumGroup>::Group;
376
377 match <#wire as ::enum_group_macros::EnumGroup>::into_group(#val) {
378 #(#match_arms),*
379 }
380 }
381 })
382}