use proc_macro2::Span;
use syn::{Attribute, Expr, ExprLit, ExprUnary, Lit, Result, UnOp};
#[derive(Debug, Clone, Default)]
pub struct FieldAttr {
pub varint: bool,
pub length_varint: bool,
pub optional: bool,
pub rest: bool,
pub element_varint: bool,
}
#[derive(Debug, Clone)]
pub struct VariantAttr {
pub id: i32,
}
fn parse_int_expr(expr: &Expr) -> syn::Result<i32> {
match expr {
Expr::Lit(ExprLit {
lit: Lit::Int(lit_int),
..
}) => lit_int.base10_parse::<i32>(),
Expr::Unary(ExprUnary {
op: UnOp::Neg(_),
expr: inner,
..
}) => {
if let Expr::Lit(ExprLit {
lit: Lit::Int(lit_int),
..
}) = inner.as_ref()
{
Ok(-lit_int.base10_parse::<i32>()?)
} else {
Err(syn::Error::new_spanned(expr, "expected integer literal"))
}
}
_ => Err(syn::Error::new_spanned(expr, "expected integer literal")),
}
}
pub fn parse_field_attr(attrs: &[Attribute]) -> Result<FieldAttr> {
let mut field_attr = FieldAttr::default();
for attr in attrs {
if !attr.path().is_ident("field") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("varint") {
field_attr.varint = true;
Ok(())
} else if meta.path.is_ident("optional") {
field_attr.optional = true;
Ok(())
} else if meta.path.is_ident("rest") {
field_attr.rest = true;
Ok(())
} else if meta.path.is_ident("length") {
let value = meta.value()?;
let lit: Lit = value.parse()?;
if let Lit::Str(s) = lit {
if s.value() == "varint" {
field_attr.length_varint = true;
Ok(())
} else {
Err(syn::Error::new_spanned(s, "expected \"varint\""))
}
} else {
Err(syn::Error::new_spanned(lit, "expected string literal"))
}
} else if meta.path.is_ident("element") {
let value = meta.value()?;
let lit: Lit = value.parse()?;
if let Lit::Str(s) = lit {
if s.value() == "varint" {
field_attr.element_varint = true;
Ok(())
} else {
Err(syn::Error::new_spanned(s, "expected \"varint\""))
}
} else {
Err(syn::Error::new_spanned(lit, "expected string literal"))
}
} else {
Err(meta.error("expected `varint`, `optional`, `rest`, `length`, or `element`"))
}
})?;
}
Ok(field_attr)
}
pub fn parse_variant_attr(attrs: &[Attribute]) -> Result<Option<VariantAttr>> {
for attr in attrs {
if !attr.path().is_ident("variant") {
continue;
}
let mut id = None;
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("id") {
let value = meta.value()?;
let expr: Expr = value.parse()?;
id = Some(parse_int_expr(&expr)?);
Ok(())
} else {
Err(meta.error("expected `id`"))
}
})?;
match id {
Some(id) => return Ok(Some(VariantAttr { id })),
None => {
return Err(syn::Error::new(
Span::call_site(),
"#[variant] requires `id` parameter",
));
}
}
}
Ok(None)
}