matched_enums_macro 1.3.0

Contains the macro for matchable enums.
Documentation
extern crate alloc;

use alloc::vec::Vec;

use proc_macro2::Span;
use syn::{
    Attribute, Expr, ExprLit, Lit, MetaNameValue, Token, Type,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
};

use super::MatchArm;
use crate::utils::try_convert_type_expr_into_type;

#[allow(
    dead_code,
    reason = "These attributes need to be parsed regardless. Might as well add them. Even if unused"
)]
#[derive(Debug, Clone)]
pub struct MatchAttribute {
    pub attrs: Vec<Attribute>,
    pub arm: MatchArm,
    pub precedence: u16,
    pub type_bind: Option<Type>,
    span: Span,
}

impl MatchAttribute {
    fn set_precedence(&mut self, val: &Expr) -> syn::Result<()> {
        self.precedence = match &val {
            Expr::Lit(ExprLit {
                lit: Lit::Int(value),
                ..
            }) => value.base10_parse()?,
            _ => {
                return Err(syn::Error::new(
                    val.span(),
                    "Must be set to a literal integer",
                ));
            }
        };

        Ok(())
    }

    fn set_type_bind(&mut self, val: &Expr) -> syn::Result<()> {
        self.type_bind = Some(try_convert_type_expr_into_type(val)?);
        Ok(())
    }

    pub fn span(&self) -> Span {
        self.span
    }
}

impl Parse for MatchAttribute {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut match_attr = Self {
            attrs: input.call(Attribute::parse_outer)?,
            arm: MatchArm::parse(input)?,
            precedence: u16::MAX,
            type_bind: None,
            span: input.span(),
        };

        let sep: Option<Token![,]> = input.parse()?;
        if sep.is_none() {
            return Ok(match_attr);
        }

        for MetaNameValue {
            ref path,
            ref value,
            ..
        } in Punctuated::<MetaNameValue, Token![,]>::parse_terminated(input)?
        {
            if path.is_ident("bind") {
                match_attr.set_type_bind(value)?;
            } else if path.is_ident("precedence") {
                match_attr.set_precedence(value)?;
            } else {
                return Err(syn::Error::new(
                    path.span(),
                    format_args!("Unknown attribute '{}'", path.get_ident().unwrap()),
                ));
            }
        }

        Ok(match_attr)
    }
}

#[cfg(test)]
mod test;