select_macro_utils/
lib.rs

1use proc_macro::{TokenStream, TokenTree};
2use proc_macro2::Span;
3use quote::quote;
4use syn::Ident;
5
6pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
7    let branches = match input.into_iter().next() {
8        Some(TokenTree::Group(group)) => group.stream().into_iter().count(),
9        _ => panic!("unexpected macro input"),
10    };
11
12    let variants = (0..branches)
13        .map(|num| Ident::new(&format!("_{}", num), Span::call_site()))
14        .collect::<Vec<_>>();
15
16    let mask = Ident::new(
17        if branches <= 8 {
18            "u8"
19        } else if branches <= 16 {
20            "u16"
21        } else if branches <= 32 {
22            "u32"
23        } else if branches <= 64 {
24            "u64"
25        } else {
26            panic!("up to 64 branches supported");
27        },
28        Span::call_site(),
29    );
30
31    TokenStream::from(quote! {
32        pub(super) enum Out<#( #variants ),*> {
33            #( #variants(#variants), )*
34            Disabled,
35        }
36
37        pub(super) type Mask = #mask;
38    })
39}
40
41pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
42    let mut input: syn::Pat = match syn::parse(input.clone()) {
43        Ok(it) => it,
44        Err(_) => return input,
45    };
46
47    clean_pattern(&mut input);
48    quote::ToTokens::into_token_stream(input).into()
49}
50
51fn clean_pattern(pat: &mut syn::Pat) {
52    match pat {
53        syn::Pat::Box(_box) => {}
54        syn::Pat::Lit(_literal) => {}
55        syn::Pat::Macro(_macro) => {}
56        syn::Pat::Path(_path) => {}
57        syn::Pat::Range(_range) => {}
58        syn::Pat::Rest(_rest) => {}
59        syn::Pat::Verbatim(_tokens) => {}
60        syn::Pat::Wild(_underscore) => {}
61        syn::Pat::Ident(ident) => {
62            ident.by_ref = None;
63            ident.mutability = None;
64            if let Some((_at, pat)) = &mut ident.subpat {
65                clean_pattern(&mut *pat);
66            }
67        }
68        syn::Pat::Or(or) => {
69            for case in or.cases.iter_mut() {
70                clean_pattern(case);
71            }
72        }
73        syn::Pat::Slice(slice) => {
74            for elem in slice.elems.iter_mut() {
75                clean_pattern(elem);
76            }
77        }
78        syn::Pat::Struct(struct_pat) => {
79            for field in struct_pat.fields.iter_mut() {
80                clean_pattern(&mut field.pat);
81            }
82        }
83        syn::Pat::Tuple(tuple) => {
84            for elem in tuple.elems.iter_mut() {
85                clean_pattern(elem);
86            }
87        }
88        syn::Pat::TupleStruct(tuple) => {
89            for elem in tuple.pat.elems.iter_mut() {
90                clean_pattern(elem);
91            }
92        }
93        syn::Pat::Reference(reference) => {
94            reference.mutability = None;
95            clean_pattern(&mut *reference.pat);
96        }
97        syn::Pat::Type(type_pat) => {
98            clean_pattern(&mut *type_pat.pat);
99        }
100        _ => {}
101    }
102}
103
104#[proc_macro]
105pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream {
106    declare_output_enum(input)
107}
108
109#[proc_macro]
110pub fn select_priv_clean_pattern(input: TokenStream) -> TokenStream {
111    clean_pattern_macro(input)
112}