use super::utils::assume_group;
use super::utils::consume_punct_if;
use crate::Error;
use crate::Result;
use crate::prelude::Delimiter;
use crate::prelude::Group;
use crate::prelude::Punct;
use crate::prelude::TokenTree;
use std::iter::Peekable;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Attribute {
pub location: AttributeLocation,
pub punct: Punct,
pub tokens: Group,
}
#[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)]
#[non_exhaustive]
pub enum AttributeLocation {
Container,
Variant,
Field,
}
impl Attribute {
pub(crate) fn try_take(
location: AttributeLocation,
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Result<Vec<Self>> {
let mut result = Vec::new();
while let Some(punct) = consume_punct_if(input, '#') {
match input.peek() {
| Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
let group = assume_group(input.next());
result.push(Self {
location,
punct,
tokens: group,
});
},
| Some(TokenTree::Group(g)) => {
return Err(Error::InvalidRustSyntax {
span: g.span(),
expected: format!("[] bracket, got {:?}", g.delimiter()),
});
},
| Some(TokenTree::Punct(p)) if p.as_char() == '#' => {
},
| token => return Error::wrong_token(token, "[] group or next # attribute"),
}
}
Ok(result)
}
}
#[test]
fn test_attributes_try_take() {
use crate::token_stream;
let mut ts1 = token_stream("struct Foo;");
let stream = &mut ts1;
assert!(
Attribute::try_take(AttributeLocation::Container, stream)
.unwrap()
.is_empty()
);
match stream.next().unwrap() {
| TokenTree::Ident(i) => assert_eq!(i, "struct"),
| x => panic!("Expected ident, found {:?}", x),
}
let mut ts2 = token_stream("#[cfg(test)] struct Foo;");
let stream = &mut ts2;
assert!(
!Attribute::try_take(AttributeLocation::Container, stream)
.unwrap()
.is_empty()
);
match stream.next().unwrap() {
| TokenTree::Ident(i) => assert_eq!(i, "struct"),
| x => panic!("Expected ident, found {:?}", x),
}
}
pub trait FromAttribute: Sized {
fn parse(group: &Group) -> Result<Option<Self>>;
}
pub trait AttributeAccess {
fn has_attribute<T: FromAttribute + PartialEq<T>>(
&self,
attrib: T,
) -> Result<bool>;
fn get_attribute<T: FromAttribute>(&self) -> Result<Option<T>>;
}
impl AttributeAccess for Vec<Attribute> {
fn has_attribute<T: FromAttribute + PartialEq<T>>(
&self,
attrib: T,
) -> Result<bool> {
for attribute in self {
let parsed = T::parse(&attribute.tokens)?;
if let Some(attribute) = parsed
&& attribute == attrib
{
return Ok(true);
}
}
Ok(false)
}
fn get_attribute<T: FromAttribute>(&self) -> Result<Option<T>> {
for attribute in self {
if let Some(attribute) = T::parse(&attribute.tokens)? {
return Ok(Some(attribute));
}
}
Ok(None)
}
}