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;