use crate::ast;
use crate::ast::{Span, Spanned};
use crate::parse::{Parse, ParseError, ParseErrorKind, Parser, Resolve, ResolveContext};
use std::collections::BTreeSet;
pub(crate) struct Attributes {
unused: BTreeSet<usize>,
attributes: Vec<ast::Attribute>,
}
impl Attributes {
pub(crate) fn new(attributes: Vec<ast::Attribute>) -> Self {
Self {
unused: attributes.iter().enumerate().map(|(i, _)| i).collect(),
attributes,
}
}
pub(crate) fn drain(&mut self) {
self.unused.clear();
}
pub(crate) fn try_parse<T>(
&mut self,
ctx: ResolveContext<'_>,
) -> Result<Option<(Span, T)>, ParseError>
where
T: Attribute + Parse,
{
let mut matched = None;
for index in self.unused.iter().copied() {
let a = match self.attributes.get(index) {
Some(a) => a,
None => continue,
};
let ident = match a.path.try_as_ident() {
Some(ident) => ident,
None => continue,
};
let ident = ident.resolve(ctx)?;
if ident != T::PATH {
continue;
}
let span = a.span();
if matched.is_some() {
return Err(ParseError::new(
span,
ParseErrorKind::MultipleMatchingAttributes { name: T::PATH },
));
}
let mut parser = Parser::from_token_stream(&a.input, a.span());
matched = Some((index, span, parser.parse::<T>()?));
parser.eof()?;
}
if let Some((index, span, matched)) = matched {
self.unused.remove(&index);
Ok(Some((span, matched)))
} else {
Ok(None)
}
}
pub(crate) fn remaining(&self) -> Option<Span> {
for i in self.unused.iter().copied() {
if let Some(a) = self.attributes.get(i) {
return Some(a.span());
}
}
None
}
}
pub(crate) trait Attribute {
const PATH: &'static str;
}
#[derive(Default)]
pub(crate) struct BuiltInArgs {
pub(crate) literal: bool,
}
#[derive(Parse)]
pub(crate) struct BuiltIn {
pub args: Option<ast::Parenthesized<ast::Ident, T![,]>>,
}
impl BuiltIn {
pub(crate) fn args(&self, ctx: ResolveContext<'_>) -> Result<BuiltInArgs, ParseError> {
let mut out = BuiltInArgs::default();
if let Some(args) = &self.args {
for (ident, _) in args {
match ident.resolve(ctx)? {
"literal" => {
out.literal = true;
}
_ => {
return Err(ParseError::msg(ident, "unsupported attribute"));
}
}
}
}
Ok(out)
}
}
impl Attribute for BuiltIn {
const PATH: &'static str = "builtin";
}
#[derive(Parse)]
pub(crate) struct Test {}
impl Attribute for Test {
const PATH: &'static str = "test";
}
#[derive(Parse)]
pub(crate) struct Bench {}
impl Attribute for Bench {
const PATH: &'static str = "bench";
}