use proc_macro2::Span; use syn::parse::{Error, Result}; use syn::spanned::Spanned; use syn::{Attribute, Ident, Lit, LitStr, Meta, NestedMeta, Path}; use crate::structures::PermissionLevel; use crate::util::{AsOption, LitExt}; use std::fmt::{self, Write}; #[derive(Debug, Clone, Copy, PartialEq)] pub enum ValueKind { // #[] Name, // #[ = ] Equals, // #[([, , , ...])] List, // #[()] SingleList, } impl fmt::Display for ValueKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ValueKind::Name => f.pad("`#[]`"), ValueKind::Equals => f.pad("`#[ = ]`"), ValueKind::List => f.pad("`#[([, , , ...])]`"), ValueKind::SingleList => f.pad("`#[()]`"), } } } fn to_ident(p: Path) -> Result { if p.segments.is_empty() { return Err(Error::new( p.span(), "cannot convert an empty path to an identifier", )); } if p.segments.len() > 1 { return Err(Error::new( p.span(), "the path must not have more than one segment", )); } if !p.segments[0].arguments.is_empty() { return Err(Error::new( p.span(), "the singular path segment must not have any arguments", )); } Ok(p.segments[0].ident.clone()) } #[derive(Debug)] pub struct Values { pub name: Ident, pub literals: Vec, pub kind: ValueKind, pub span: Span, } impl Values { #[inline] pub fn new(name: Ident, kind: ValueKind, literals: Vec, span: Span) -> Self { Values { name, literals, kind, span, } } } pub fn parse_values(attr: &Attribute) -> Result { let meta = attr.parse_meta()?; match meta { Meta::Path(path) => { let name = to_ident(path)?; Ok(Values::new(name, ValueKind::Name, Vec::new(), attr.span())) } Meta::List(meta) => { let name = to_ident(meta.path)?; let nested = meta.nested; if nested.is_empty() { return Err(Error::new(attr.span(), "list cannot be empty")); } let mut lits = Vec::with_capacity(nested.len()); for meta in nested { match meta { NestedMeta::Lit(l) => lits.push(l), NestedMeta::Meta(m) => match m { Meta::Path(path) => { let i = to_ident(path)?; lits.push(Lit::Str(LitStr::new(&i.to_string(), i.span()))) } Meta::List(_) | Meta::NameValue(_) => { return Err(Error::new(attr.span(), "cannot nest a list; only accept literals and identifiers at this level")) } }, } } let kind = if lits.len() == 1 { ValueKind::SingleList } else { ValueKind::List }; Ok(Values::new(name, kind, lits, attr.span())) } Meta::NameValue(meta) => { let name = to_ident(meta.path)?; let lit = meta.lit; Ok(Values::new(name, ValueKind::Equals, vec![lit], attr.span())) } } } #[derive(Debug, Clone)] struct DisplaySlice<'a, T>(&'a [T]); impl<'a, T: fmt::Display> fmt::Display for DisplaySlice<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut iter = self.0.iter().enumerate(); match iter.next() { None => f.write_str("nothing")?, Some((idx, elem)) => { write!(f, "{}: {}", idx, elem)?; for (idx, elem) in iter { f.write_char('\n')?; write!(f, "{}: {}", idx, elem)?; } } } Ok(()) } } #[inline] fn is_form_acceptable(expect: &[ValueKind], kind: ValueKind) -> bool { if expect.contains(&ValueKind::List) && kind == ValueKind::SingleList { true } else { expect.contains(&kind) } } #[inline] fn validate(values: &Values, forms: &[ValueKind]) -> Result<()> { if !is_form_acceptable(forms, values.kind) { return Err(Error::new( values.span, // Using the `_args` version here to avoid an allocation. format_args!( "the attribute must be in of these forms:\n{}", DisplaySlice(forms) ), )); } Ok(()) } #[inline] pub fn parse(values: Values) -> Result { T::parse(values) } pub trait AttributeOption: Sized { fn parse(values: Values) -> Result; } impl AttributeOption for Vec { fn parse(values: Values) -> Result { validate(&values, &[ValueKind::List])?; Ok(values .literals .into_iter() .map(|lit| lit.to_str()) .collect()) } } impl AttributeOption for String { #[inline] fn parse(values: Values) -> Result { validate(&values, &[ValueKind::Equals, ValueKind::SingleList])?; Ok(values.literals[0].to_str()) } } impl AttributeOption for bool { #[inline] fn parse(values: Values) -> Result { validate(&values, &[ValueKind::Name, ValueKind::SingleList])?; Ok(values.literals.get(0).map_or(true, |l| l.to_bool())) } } impl AttributeOption for Ident { #[inline] fn parse(values: Values) -> Result { validate(&values, &[ValueKind::SingleList])?; Ok(values.literals[0].to_ident()) } } impl AttributeOption for Vec { #[inline] fn parse(values: Values) -> Result { validate(&values, &[ValueKind::List])?; Ok(values.literals.into_iter().map(|l| l.to_ident()).collect()) } } impl AttributeOption for Option { fn parse(values: Values) -> Result { validate(&values, &[ValueKind::Name, ValueKind::Equals, ValueKind::SingleList])?; Ok(values.literals.get(0).map(|l| l.to_str())) } } impl AttributeOption for PermissionLevel { fn parse(values: Values) -> Result { validate(&values, &[ValueKind::SingleList])?; Ok(values.literals.get(0).map(|l| PermissionLevel::from_str(&*l.to_str()).unwrap()).unwrap()) } } impl AttributeOption for AsOption { #[inline] fn parse(values: Values) -> Result { Ok(AsOption(Some(T::parse(values)?))) } } macro_rules! attr_option_num { ($($n:ty),*) => { $( impl AttributeOption for $n { fn parse(values: Values) -> Result { validate(&values, &[ValueKind::SingleList])?; Ok(match &values.literals[0] { Lit::Int(l) => l.base10_parse::<$n>()?, l => { let s = l.to_str(); // Use `as_str` to guide the compiler to use `&str`'s parse method. // We don't want to use our `parse` method here (`impl AttributeOption for String`). match s.as_str().parse::<$n>() { Ok(n) => n, Err(_) => return Err(Error::new(l.span(), "invalid integer")), } } }) } } impl AttributeOption for Option<$n> { #[inline] fn parse(values: Values) -> Result { <$n as AttributeOption>::parse(values).map(Some) } } )* } } attr_option_num!(u16, u32, usize);