repetitive 0.2.0

Macro for generating repetitive code
Documentation
use proc_macro2::{Delimiter, Group, TokenStream};
use syn::{Token, parse::ParseStream};

use super::*;

#[derive(Debug, Clone)]
pub struct FragmentMatch {
    pub expr: Expr,
}

impl ContextParse for FragmentMatch {
    fn ctx_parse(input: ParseStream, ctx: &mut Context) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let match_token = <Token![match]>::ctx_parse(input, ctx)?;

        let expr = Box::new(Expr::ctx_parse(input, ctx)?);

        let group = Group::ctx_parse(input, ctx)?;
        if group.delimiter() != Delimiter::Brace {
            return Err(Error::ParseError(syn::Error::new(
                group.span(),
                "expected a brace-delimited block",
            )));
        }

        let match_arms_parse_fn = |input: ParseStream, ctx: &mut Context| {
            let mut arms = Vec::new();

            while !input.is_empty() {
                let pat_span = input.span();
                let pat = Pattern::ctx_parse(input, ctx)?;

                let condition = if input.peek(Token![if]) {
                    <Token![if]>::ctx_parse(input, ctx)?;

                    Some(Expr::ctx_parse(input, ctx)?)
                } else {
                    None
                };

                <Token![=>]>::ctx_parse(input, ctx)?;

                let body_group = Group::ctx_parse(input, ctx)?;
                if body_group.delimiter() != Delimiter::Brace {
                    return Err(Error::ParseError(syn::Error::new(
                        body_group.span(),
                        "expected a brace-delimited block",
                    )));
                }

                let body = Tokens::ctx_parse.ctx_parse2(body_group.stream(), ctx)?;

                arms.push(ExprMatchArm {
                    pat,
                    condition,
                    body: Value {
                        span: group.span(),
                        kind: ValueKind::Tokens(body),
                    }
                    .into_expr(),
                    unused_arm_warning: ctx.push_warning(Warning::UnusedMatchArm(pat_span)),
                });

                if let Some(token) = <Option<Token![,]>>::ctx_parse(input, ctx)? {
                    ctx.push_warning(Warning::UnnecessaryPunct {
                        span: token.span,
                        punct: ",",
                    });
                }
            }

            Ok::<_, Error>(arms)
        };

        let match_arms = match_arms_parse_fn.ctx_parse2(group.stream(), ctx)?;

        let expr = Expr {
            span: match_token.span,
            kind: ExprKind::Match(ExprMatch {
                match_token,
                expr,
                match_arms,
            }),
        };

        Ok(Self { expr })
    }
}

impl Peek for FragmentMatch {
    fn peek(input: ParseStream) -> bool {
        input.peek(Token![match])
    }
}

impl Optimize for FragmentMatch {
    fn optimize(&mut self, ctx: &mut Context) {
        self.expr.optimize(ctx);
    }
}

impl Expand for FragmentMatch {
    fn expand(&self, output: &mut TokenStream, ctx: &mut Context, namespace: &Namespace) {
        self.expr.expand(output, ctx, namespace);
    }
}