ruleset-macros 2.0.1

This crate shouldn't be used directly. Check `ruleset`.
Documentation
use super::*;
use proc_macro2::TokenStream;
use proc_macro_error::emit_error;
use quote::quote;
use quote::quote_spanned;
use std::ops::Deref;
use std::ops::DerefMut;
use syn::parse_quote_spanned;
use syn::spanned::Spanned;
use syn::token::At;
use syn::token::Ref;
use syn::visit_mut::visit_pat_mut;
use syn::visit_mut::VisitMut;
use syn::Ident;
use syn::Pat;
use syn::PatIdent;
use syn::PatOr;
use syn::PatReference;

#[derive(Debug)]
pub struct ReplacePat(Vec<ReplacePatItem>);

#[derive(Debug)]
pub enum ReplacePatItem {
	Reference(Ident, Pat),
	Or(Ident, Vec<(Pat, ReplacePat)>),
	Ident(Ident, Ident),
}

impl Deref for ReplacePat {
	type Target = Vec<ReplacePatItem>;
	fn deref(&self) -> &Self::Target { &self.0 }
}

impl DerefMut for ReplacePat {
	fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

impl VisitMut for ReplacePat {
	fn visit_pat_mut(&mut self, i: &mut Pat) {
		match i {
			Pat::Reference(PatReference { pat, .. }) => {
				let sym = gensym(pat.span());
				let mut pat = *pat.clone();
				self.visit_pat_mut(&mut pat);
				*i = parse_quote_spanned!(pat.span() => ref #sym);
				self.push(ReplacePatItem::Reference(sym, pat));
			}
			Pat::Or(PatOr { cases, .. }) => {
				let mut replacements = Vec::new();
				let sym = gensym(cases.span());
				for pat in cases.iter() {
					let mut pat = pat.clone();
					let mut replacement = ReplacePat::new();
					replacement.visit_pat_mut(&mut pat);
					replacements.push((pat, replacement));
				}
				*i = parse_quote_spanned!(cases.span() => ref #sym);
				self.push(ReplacePatItem::Or(sym, replacements));
			}
			Pat::Ident(PatIdent { ident, mutability, by_ref, subpat, .. }) => {
				if mutability.is_some() {
					emit_error!(
						mutability,
						"captured variables in antecedent patterns can't be mutable"
					)
				}
				if !ident.to_string().chars().any(|char| char.is_ascii_uppercase()) {
					let sym = gensym(ident.span());
					let ident = ident.clone();
					let by_ref = by_ref.clone();
					let subpat = subpat.clone();
					*i = if let Some((at, subpat)) = subpat {
						parse_quote_spanned!(ident.span() => #by_ref #sym #at #subpat)
					} else {
						parse_quote_spanned!(ident.span() => #by_ref #sym)
					};
					self.push(ReplacePatItem::Ident(sym, ident));
				}
			}
			_ => {}
		}

		visit_pat_mut(self, i);
	}
}

impl ReplacePat {
	pub fn new() -> Self { Self(Vec::new()) }

	pub fn expand(&self, then: TokenStream, context: &[&ReplacePat]) -> TokenStream {
		self.iter().fold(then, |then, item| {
			use ReplacePatItem::*;
			match item {
				Reference(sym, pat) => {
					let pat = if let Pat::Lit(_) = pat {
						quote! { #pat }
					} else {
						quote_spanned! { pat.span() => &#pat }
					};
					let expr =
						quote_spanned! { pat.span() => let #pat = std::ops::Deref::deref(#sym) };
					quote! {
						if #expr { #then }
					}
				}
				Or(sym, replacements) => {
					let arms = replacements.iter().map(|(pat, replacement)| {
						let then = replacement.expand(then.clone(), context);
						quote_spanned! { pat.span() => &#pat => #then }
					});
					quote! { match #sym { #(#arms,)* _ => {} } }
				}
				Ident(sym, ident, ..) => {
					let sym_target = context.iter().find_map(|replace_pat| {
						replace_pat.iter().find_map(|item| match item {
							Ident(sym_target, ident_target, ..)
								if ident_target == ident && sym_target != sym =>
								Some(sym_target),
							_ => None,
						})
					});
					if let Some(sym_target) = sym_target {
						quote! { if #sym == #sym_target { #then } }
					} else {
						quote! { { #[allow(unused_variables)] let #ident = #sym; #then } }
					}
				}
			}
		})
	}
}