metamatch 0.3.0

A proc-macro for generating repetitive match arms.
Documentation
use proc_macro::{Span, TokenStream};

use super::ast::{Context, ScopeKind};

pub trait IntoIterIntoVec {
    type Item;
    fn into_vec(self) -> Vec<Self::Item>;
}
impl<I: IntoIterator> IntoIterIntoVec for I {
    type Item = I::Item;

    fn into_vec(self) -> Vec<Self::Item> {
        self.into_iter().collect()
    }
}

pub fn unquote(body: TokenStream) -> TokenStream {
    let body = body.into_vec();
    let mut ctx = Context::default();

    ctx.push_dummy_scope(ScopeKind::Unquoted);
    let expr = ctx.parse_body_deny_trailing(Span::call_site(), &body);
    ctx.scopes.pop();

    ctx.eval_to_token_stream(Span::call_site(), &expr)
}

pub fn quote(body: TokenStream) -> TokenStream {
    let body = body.into_vec();
    let mut ctx = Context::default();

    ctx.push_dummy_scope(ScopeKind::Quoted);
    let Ok(exprs) = ctx.parse_raw_block_to_exprs(Span::call_site(), &body)
    else {
        return ctx.expand_errors();
    };
    ctx.scopes.pop();

    ctx.eval_to_token_stream(Span::call_site(), &exprs)
}

pub fn replicate(attrib: TokenStream, body: TokenStream) -> TokenStream {
    let attrib = attrib.into_vec();
    let mut ctx = Context::default();
    ctx.push_dummy_scope(ScopeKind::Unquoted);
    let (mut exprs, rest, trailing_block) =
        ctx.parse_body(Span::call_site(), &attrib, true);

    if !rest.is_empty() && ctx.errors.is_empty() {
        let tb = trailing_block.expect("rest without trailing block");
        ctx.error(
            attrib[attrib.len() - rest.len() - 1].span(),
            format!("template tag `{}` is never closed", tb.to_str()),
        );
        return ctx.expand_errors();
    }

    let Some(trailing_block) = trailing_block else {
        ctx.error(
            exprs.last().map(|e| e.span()).unwrap_or(Span::call_site()),
            "replicate ignores the attribute body",
        );
        return ctx.expand_errors();
    };

    let body = body.into_vec();
    let Ok(contents) = ctx.parse_raw_block_to_exprs(Span::call_site(), &body)
    else {
        return ctx.expand_errors();
    };

    ctx.close_expr_after_trailing_body(&mut exprs, trailing_block, contents);

    ctx.pop_scope();
    ctx.pop_scope();

    ctx.eval_to_token_stream(Span::call_site(), &exprs)
}

pub fn metamatch(body: TokenStream) -> TokenStream {
    let body = body.into_vec();
    let mut ctx = Context::default();

    ctx.push_dummy_scope(ScopeKind::Metamatch);

    let Ok(exprs) = ctx.parse_raw_block_to_exprs(Span::call_site(), &body)
    else {
        return ctx.expand_errors();
    };
    ctx.scopes.pop();

    ctx.eval_to_token_stream(Span::call_site(), &exprs)
}