| use proc_macro::{TokenStream, TokenTree}; |
| use proc_macro2::Span; |
| use quote::quote; |
| use syn::Ident; |
| |
| pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream { |
| // passed in is: `(_ _ _)` with one `_` per branch |
| let branches = match input.into_iter().next() { |
| Some(TokenTree::Group(group)) => group.stream().into_iter().count(), |
| _ => panic!("unexpected macro input"), |
| }; |
| |
| let variants = (0..branches) |
| .map(|num| Ident::new(&format!("_{}", num), Span::call_site())) |
| .collect::<Vec<_>>(); |
| |
| // Use a bitfield to track which futures completed |
| let mask = Ident::new( |
| if branches <= 8 { |
| "u8" |
| } else if branches <= 16 { |
| "u16" |
| } else if branches <= 32 { |
| "u32" |
| } else if branches <= 64 { |
| "u64" |
| } else { |
| panic!("up to 64 branches supported"); |
| }, |
| Span::call_site(), |
| ); |
| |
| TokenStream::from(quote! { |
| pub(super) enum Out<#( #variants ),*> { |
| #( #variants(#variants), )* |
| // Include a `Disabled` variant signifying that all select branches |
| // failed to resolve. |
| Disabled, |
| } |
| |
| pub(super) type Mask = #mask; |
| }) |
| } |
| |
| pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream { |
| // If this isn't a pattern, we return the token stream as-is. The select! |
| // macro is using it in a location requiring a pattern, so an error will be |
| // emitted there. |
| let mut input: syn::Pat = match syn::parse(input.clone()) { |
| Ok(it) => it, |
| Err(_) => return input, |
| }; |
| |
| clean_pattern(&mut input); |
| quote::ToTokens::into_token_stream(input).into() |
| } |
| |
| // Removes any occurrences of ref or mut in the provided pattern. |
| fn clean_pattern(pat: &mut syn::Pat) { |
| match pat { |
| syn::Pat::Box(_box) => {} |
| syn::Pat::Lit(_literal) => {} |
| syn::Pat::Macro(_macro) => {} |
| syn::Pat::Path(_path) => {} |
| syn::Pat::Range(_range) => {} |
| syn::Pat::Rest(_rest) => {} |
| syn::Pat::Verbatim(_tokens) => {} |
| syn::Pat::Wild(_underscore) => {} |
| syn::Pat::Ident(ident) => { |
| ident.by_ref = None; |
| ident.mutability = None; |
| if let Some((_at, pat)) = &mut ident.subpat { |
| clean_pattern(&mut *pat); |
| } |
| } |
| syn::Pat::Or(or) => { |
| for case in or.cases.iter_mut() { |
| clean_pattern(case); |
| } |
| } |
| syn::Pat::Slice(slice) => { |
| for elem in slice.elems.iter_mut() { |
| clean_pattern(elem); |
| } |
| } |
| syn::Pat::Struct(struct_pat) => { |
| for field in struct_pat.fields.iter_mut() { |
| clean_pattern(&mut field.pat); |
| } |
| } |
| syn::Pat::Tuple(tuple) => { |
| for elem in tuple.elems.iter_mut() { |
| clean_pattern(elem); |
| } |
| } |
| syn::Pat::TupleStruct(tuple) => { |
| for elem in tuple.pat.elems.iter_mut() { |
| clean_pattern(elem); |
| } |
| } |
| syn::Pat::Reference(reference) => { |
| reference.mutability = None; |
| clean_pattern(&mut *reference.pat); |
| } |
| syn::Pat::Type(type_pat) => { |
| clean_pattern(&mut *type_pat.pat); |
| } |
| _ => {} |
| } |
| } |