unpat 0.1.3

Unboxing the pattern with easy syntax.
Documentation
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Expr, Pat, PatTupleStruct, Token};

extern crate proc_macro2;

struct Unpattern {
    idents: Vec<Ident>,
    pat: Pat,
    expr: Expr,
}

fn traverse_pat(pat: &Pat, idents: &mut Vec<Ident>) {
    match pat {
        Pat::Box(_) => println!("box"),
        Pat::Ident(ident) => {
            idents.push(ident.ident.clone());
            ident.subpat.iter().for_each(|(_, p)| {
                traverse_pat(p, idents);
            });
        }
        Pat::Lit(_) => println!("lit"),
        Pat::Macro(_) => println!("macro"),
        Pat::Or(_) => println!("or"),
        Pat::Path(_) => println!("path"),
        Pat::Range(_) => println!("range"),
        Pat::Reference(_) => println!("reference"),
        Pat::Rest(_) => println!("rest"),
        Pat::Slice(_) => println!("slice"),
        Pat::Struct(s) => s.fields.iter().for_each(|f| {
            traverse_pat(&f.pat, idents);
        }),
        Pat::Tuple(_) => println!("tuple"),
        Pat::TupleStruct(PatTupleStruct { pat, .. }) => {
            pat.elems.iter().for_each(|p| traverse_pat(p, idents));
        }
        Pat::Type(_) => println!("type"),
        Pat::Verbatim(_) => println!("verbatim"),
        Pat::Wild(_) => println!("wild"),
        _ => println!("other"),
    }
}

impl Parse for Unpattern {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let pat = input.parse::<Pat>()?;
        input.parse::<Token![<-]>()?;
        let expr = input.parse::<Expr>()?;
        let mut idents = vec![];
        traverse_pat(&pat, &mut idents);
        Ok(Unpattern { idents, pat, expr })
    }
}

impl ToTokens for Unpattern {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        let idents = &self.idents;
        let pat = &self.pat;
        let expr = &self.expr;

        let t = quote! {
            #[allow(unused_parens)]
            #[allow(irrefutable_let_patterns)]
            #[allow(clippy::redundant_pattern_matching)]
            let (#(#idents),*) = if let #pat = #expr {
                (#(#idents),*)
            } else {
                unreachable!("The pattern isn't match with the expression");
            };
        };
        tokens.extend(t);
    }
}

struct TryUnpattern {
    idents: Vec<Ident>,
    pat: Pat,
    expr: Expr,
    error: Expr,
}

impl Parse for TryUnpattern {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let pat = input.parse::<Pat>()?;
        input.parse::<Token![<-]>()?;
        let expr = input.parse::<Expr>()?;
        input.parse::<Token![,]>()?;
        let error = input.parse::<Expr>()?;
        let mut idents = vec![];
        traverse_pat(&pat, &mut idents);
        Ok(TryUnpattern {
            idents,
            pat,
            expr,
            error,
        })
    }
}

impl ToTokens for TryUnpattern {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        let idents = &self.idents;
        let pat = &self.pat;
        let expr = &self.expr;
        let error = &self.error;

        let t = quote! {
            #[allow(unused_parens)]
            #[allow(irrefutable_let_patterns)]
            #[allow(clippy::redundant_pattern_matching)]
            let (#(#idents),*) = if let #pat = #expr {
                (#(#idents),*)
            } else {
                return ::core::result::Result::Err(#error);
            };
        };
        tokens.extend(t);
    }
}

#[proc_macro]
pub fn unpat(token: TokenStream) -> TokenStream {
    let v = parse_macro_input!(token as Unpattern);
    v.to_token_stream().into()
}

#[proc_macro]
pub fn try_unpat(token: TokenStream) -> TokenStream {
    let v = parse_macro_input!(token as TryUnpattern);
    v.to_token_stream().into()
}