use crate::ast;
use crate::attrs::Attribute;
use crate::macros::Storage;
use crate::parsing::{Parse, ParseError, ParseErrorKind, Parser, Resolve as _};
use crate::Spanned as _;
use runestick::Source;
use runestick::Span;
use std::collections::BTreeSet;
use std::sync::Arc;
pub(crate) struct Attributes {
unused: BTreeSet<usize>,
attributes: Vec<ast::Attribute>,
storage: Storage,
source: Arc<Source>,
}
impl Attributes {
pub(crate) fn new(
attributes: Vec<ast::Attribute>,
storage: Storage,
source: Arc<Source>,
) -> Self {
Self {
unused: attributes.iter().enumerate().map(|(i, _)| i).collect(),
attributes,
storage,
source,
}
}
pub(crate) fn drain(&mut self) {
self.unused.clear();
}
pub(crate) fn try_parse<T>(&mut self) -> 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(&self.storage, &self.source)?;
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);
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
}
}