rune/ast/
attribute.rs
1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6 rt::<ast::Attribute>("#[foo = \"foo\"]");
7 rt::<ast::Attribute>("#[foo()]");
8 rt::<ast::Attribute>("#![foo]");
9 rt::<ast::Attribute>("#![cfg(all(feature = \"potato\"))]");
10 rt::<ast::Attribute>("#[x+1]");
11
12 const TEST_STRINGS: &[&str] = &[
13 "#[foo]",
14 "#[a::b::c]",
15 "#[foo = \"hello world\"]",
16 "#[foo = 1]",
17 "#[foo = 1.3]",
18 "#[foo = true]",
19 "#[foo = b\"bytes\"]",
20 "#[foo = (1, 2, \"string\")]",
21 "#[foo = #{\"a\": 1} ]",
22 r#"#[foo = Fred {"a": 1} ]"#,
23 r#"#[foo = a::Fred {"a": #{ "b": 2 } } ]"#,
24 "#[bar()]",
25 "#[bar(baz)]",
26 "#[derive(Debug, PartialEq, PartialOrd)]",
27 "#[tracing::instrument(skip(non_debug))]",
28 "#[zanzibar(a = \"z\", both = false, sasquatch::herring)]",
29 r#"#[doc = "multiline \
30 docs are neat"
31 ]"#,
32 ];
33
34 for s in TEST_STRINGS.iter() {
35 rt::<ast::Attribute>(s);
36 let withbang = s.replacen("#[", "#![", 1);
37 rt::<ast::Attribute>(&withbang);
38 }
39}
40
41#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
46#[non_exhaustive]
47pub struct Attribute {
48 pub hash: T![#],
50 #[rune(option)]
52 pub style: AttrStyle,
53 pub open: T!['['],
55 pub path: ast::Path,
57 #[rune(iter)]
59 pub input: TokenStream,
60 pub close: T![']'],
62}
63
64impl Attribute {
65 pub(crate) fn input_span(&self) -> Span {
66 self.input
67 .option_span()
68 .unwrap_or_else(|| self.close.span.head())
69 }
70}
71
72impl Parse for Attribute {
73 fn parse(p: &mut Parser<'_>) -> Result<Self> {
74 let hash = p.parse()?;
75 let style = p.parse()?;
76 let open = p.parse()?;
77 let path = p.parse()?;
78
79 let close;
80
81 let mut level = 1;
82 let mut input = TokenStream::new();
83
84 loop {
85 let token = p.next()?;
86
87 match token.kind {
88 K!['['] => level += 1,
89 K![']'] => {
90 level -= 1;
91 }
92 _ => (),
93 }
94
95 if level == 0 {
96 close = ast::CloseBracket { span: token.span };
97 break;
98 }
99
100 input.push(token)?;
101 }
102
103 Ok(Attribute {
104 hash,
105 style,
106 open,
107 path,
108 input,
109 close,
110 })
111 }
112}
113
114impl Peek for Attribute {
115 fn peek(p: &mut Peeker<'_>) -> bool {
116 match (p.nth(0), p.nth(1)) {
117 (K![#], K![!]) => true,
118 (K![#], K!['[']) => true,
119 _ => false,
120 }
121 }
122}
123
124impl IntoExpectation for Attribute {
125 fn into_expectation(self) -> Expectation {
126 Expectation::Description(match &self.style {
127 AttrStyle::Inner => "inner attribute",
128 AttrStyle::Outer(_) => "outer attribute",
129 })
130 }
131}
132
133#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, OptionSpanned, ToTokens)]
135#[try_clone(copy)]
136#[non_exhaustive]
137pub enum AttrStyle {
138 Inner,
140 Outer(T![!]),
142}
143
144impl Parse for AttrStyle {
145 fn parse(p: &mut Parser<'_>) -> Result<Self> {
146 Ok(if p.peek::<T![!]>()? {
147 Self::Outer(p.parse()?)
148 } else {
149 Self::Inner
150 })
151 }
152}
153
154#[non_exhaustive]
157pub(crate) struct OuterAttribute;
158
159impl Peek for OuterAttribute {
160 fn peek(p: &mut Peeker<'_>) -> bool {
161 match (p.nth(0), p.nth(1)) {
162 (K![#], K![!]) => true,
163 _ => false,
164 }
165 }
166}