extract_variant_internal/
lib.rs

1//! Internal lib for [variant](https://crates.io/crates/extract-variant).
2
3use proc_macro::TokenStream;
4use proc_macro2::Ident;
5use quote::{quote, ToTokens};
6use syn::visit::Visit;
7use syn::{parse_macro_input, Error, Pat, PatIdent, PatOr};
8
9#[derive(Default, Debug)]
10struct VisitPatIdent<'a> {
11    idents: Vec<&'a Ident>,
12    pat_or: Option<proc_macro2::TokenStream>,
13}
14
15impl<'ast> Visit<'ast> for VisitPatIdent<'ast> {
16    fn visit_pat_ident(&mut self, node: &'ast PatIdent) {
17        // Since `None` cannot be distinguish from an ident, we check for and
18        // ignore it here.
19        if node.ident != "None" {
20            self.idents.push(&node.ident);
21        }
22        syn::visit::visit_pat_ident(self, node)
23    }
24
25    fn visit_pat_or(&mut self, node: &'ast PatOr) {
26        self.pat_or = Some(node.to_token_stream())
27        // Since we're going to error the macro we can stop visiting.
28    }
29}
30
31#[proc_macro]
32pub fn extract_variant_assign(input: TokenStream) -> TokenStream {
33    let input = parse_macro_input!(input as Pat);
34    let mut visitor = VisitPatIdent::default();
35    visitor.visit_pat(&input);
36
37    if let Some(tokens) = visitor.pat_or {
38        let msg = "`variant` cannot match `or` patterns";
39        let err = Error::new_spanned(tokens, msg).to_compile_error();
40        return TokenStream::from(err);
41    }
42
43    visitor.idents.sort_unstable();
44    let tokens = match visitor.idents.as_slice() {
45        [id] => quote! {
46            #id
47        },
48        ids => quote! {
49            (#(#ids),*)
50        },
51    };
52
53    TokenStream::from(tokens)
54}