use crate::ast::prelude::*;
#[test]
#[cfg(not(miri))]
fn ast_parse() {
rt::<ast::Attribute>("#[foo = \"foo\"]");
rt::<ast::Attribute>("#[foo()]");
rt::<ast::Attribute>("#![foo]");
rt::<ast::Attribute>("#![cfg(all(feature = \"potato\"))]");
rt::<ast::Attribute>("#[x+1]");
const TEST_STRINGS: &[&str] = &[
"#[foo]",
"#[a::b::c]",
"#[foo = \"hello world\"]",
"#[foo = 1]",
"#[foo = 1.3]",
"#[foo = true]",
"#[foo = b\"bytes\"]",
"#[foo = (1, 2, \"string\")]",
"#[foo = #{\"a\": 1} ]",
r#"#[foo = Fred {"a": 1} ]"#,
r#"#[foo = a::Fred {"a": #{ "b": 2 } } ]"#,
"#[bar()]",
"#[bar(baz)]",
"#[derive(Debug, PartialEq, PartialOrd)]",
"#[tracing::instrument(skip(non_debug))]",
"#[zanzibar(a = \"z\", both = false, sasquatch::herring)]",
r#"#[doc = "multiline \
docs are neat"
]"#,
];
for s in TEST_STRINGS.iter() {
rt::<ast::Attribute>(s);
let withbang = s.replacen("#[", "#![", 1);
rt::<ast::Attribute>(&withbang);
}
}
#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub struct Attribute {
pub hash: T![#],
#[rune(option)]
pub style: AttrStyle,
pub open: T!['['],
pub path: ast::Path,
#[rune(iter)]
pub input: TokenStream,
pub close: T![']'],
}
impl Attribute {
pub(crate) fn input_span(&self) -> Span {
self.input
.option_span()
.unwrap_or_else(|| self.close.span.head())
}
}
impl Parse for Attribute {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
let hash = p.parse()?;
let style = p.parse()?;
let open = p.parse()?;
let path = p.parse()?;
let close;
let mut level = 1;
let mut input = TokenStream::new();
loop {
let token = p.next()?;
match token.kind {
K!['['] => level += 1,
K![']'] => {
level -= 1;
}
_ => (),
}
if level == 0 {
close = ast::CloseBracket { span: token.span };
break;
}
input.push(token)?;
}
Ok(Attribute {
hash,
style,
open,
path,
input,
close,
})
}
}
impl Peek for Attribute {
fn peek(p: &mut Peeker<'_>) -> bool {
match (p.nth(0), p.nth(1)) {
(K![#], K![!]) => true,
(K![#], K!['[']) => true,
_ => false,
}
}
}
impl IntoExpectation for Attribute {
fn into_expectation(self) -> Expectation {
Expectation::Description(match &self.style {
AttrStyle::Inner => "inner attribute",
AttrStyle::Outer(_) => "outer attribute",
})
}
}
#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, OptionSpanned, ToTokens)]
#[try_clone(copy)]
#[non_exhaustive]
pub enum AttrStyle {
Inner,
Outer(T![!]),
}
impl Parse for AttrStyle {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
Ok(if p.peek::<T![!]>()? {
Self::Outer(p.parse()?)
} else {
Self::Inner
})
}
}
#[non_exhaustive]
pub(crate) struct OuterAttribute;
impl Peek for OuterAttribute {
fn peek(p: &mut Peeker<'_>) -> bool {
match (p.nth(0), p.nth(1)) {
(K![#], K![!]) => true,
_ => false,
}
}
}