match-by-hash 1.0.1

Match statement, but for any value and with a hash function
Documentation
use syn::{Expr, parse::{Parse, ParseStream}, Token, ExprCall};

pub struct Arm {
    pub key: Expr,
    pub value: Expr,
}

pub struct Input {
    pub hash_function: Expr,
    pub target: Expr,
    pub arms: Vec<Arm>,
    pub default_arm: Expr,
}

enum MatchArm {
    Wild(Expr),
    Pat(Arm),
}

impl Parse for MatchArm {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let key = if input.peek(Token![_]) {
            input.parse::<Token![_]>().unwrap();
            None
        } else {
            Some(input.parse::<Expr>()?)
        };

        input.parse::<Token![=>]>()?;
        let value = input.parse::<Expr>()?;

        let result = match key {
            Some(key) => MatchArm::Pat(Arm { key, value }),
            None => MatchArm::Wild(value),
        };
        Ok(result)
    }
}

impl Parse for Input {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        input.parse::<Token![match]>()?;
        let function = input.parse::<ExprCall>()?;

        if function.args.len() != 1 {
            return Err(input.error("Expected a function call with 1 argument"));
        }
        let target = function.args.into_iter().next().unwrap();
        let hash_function = *function.func;

        let match_inner;
        let _ = syn::braced!(match_inner in input);

        let match_arms = match_inner.parse_terminated(MatchArm::parse, Token![,])?;

        let mut arms = Vec::new();
        let mut default_arm = None;
        for i in match_arms {
            match i {
                MatchArm::Wild(expr) => {
                    if default_arm.replace(expr).is_some() {
                        return Err(input.error("Wild pattern provided twice"));
                    }
                },
                MatchArm::Pat(arm) => {
                    arms.push(arm);
                },
            }
        }
        let default_arm = default_arm
            .ok_or_else(|| input.error("Wild pattern (_) wasn't provided"))?;

        Ok(Self {
            hash_function,
            target,
            arms,
            default_arm,
        })
    }
}