rune/ast/
item.rs

1use core::mem::take;
2
3use crate::ast::prelude::*;
4
5use super::Attribute;
6
7/// A declaration.
8#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
9#[non_exhaustive]
10pub enum Item {
11    /// A use declaration.
12    Use(ast::ItemUse),
13    /// A function declaration.
14    // large variant, so boxed
15    Fn(ast::ItemFn),
16    /// An enum declaration.
17    Enum(ast::ItemEnum),
18    /// A struct declaration.
19    Struct(ast::ItemStruct),
20    /// An impl declaration.
21    Impl(ast::ItemImpl),
22    /// A module declaration.
23    Mod(ast::ItemMod),
24    /// A const declaration.
25    Const(ast::ItemConst),
26    /// A macro call expanding into an item.
27    MacroCall(ast::MacroCall),
28}
29
30impl Item {
31    /// Get the item's attributes
32    pub(crate) fn attributes(&self) -> &[ast::Attribute] {
33        match self {
34            Self::Use(item) => &item.attributes,
35            Self::Fn(item) => &item.attributes,
36            Self::Enum(item) => &item.attributes,
37            Self::Struct(item) => &item.attributes,
38            Self::Impl(item) => &item.attributes,
39            Self::Mod(item) => &item.attributes,
40            Self::Const(item) => &item.attributes,
41            Self::MacroCall(item) => &item.attributes,
42        }
43    }
44    /// Get the item's attributes mutably
45    pub(crate) fn attributes_mut(&mut self) -> &mut Vec<ast::Attribute> {
46        match self {
47            Self::Use(item) => &mut item.attributes,
48            Self::Fn(item) => &mut item.attributes,
49            Self::Enum(item) => &mut item.attributes,
50            Self::Struct(item) => &mut item.attributes,
51            Self::Impl(item) => &mut item.attributes,
52            Self::Mod(item) => &mut item.attributes,
53            Self::Const(item) => &mut item.attributes,
54            Self::MacroCall(item) => &mut item.attributes,
55        }
56    }
57
58    /// Indicates if the declaration needs a semi-colon or not.
59    pub(crate) fn needs_semi_colon(&self) -> bool {
60        match self {
61            Self::Use(..) => true,
62            Self::Struct(st) => st.needs_semi_colon(),
63            Self::Const(..) => true,
64            _ => false,
65        }
66    }
67
68    /// Test if declaration is suitable inside of a file.
69    pub(crate) fn peek_as_item(p: &mut Peeker<'_>) -> bool {
70        match p.nth(0) {
71            K![use] => true,
72            K![enum] => true,
73            K![struct] => true,
74            K![impl] => true,
75            K![async] => matches!(p.nth(1), K![fn]),
76            K![fn] => true,
77            K![mod] => true,
78            K![const] => true,
79            _ => false,
80        }
81    }
82
83    /// Parse an Item attaching the given meta and optional path.
84    pub(crate) fn parse_with_meta_path(
85        p: &mut Parser<'_>,
86        mut attributes: Vec<ast::Attribute>,
87        mut visibility: ast::Visibility,
88        path: Option<ast::Path>,
89    ) -> Result<Self> {
90        let item = if let Some(path) = path {
91            Self::MacroCall(ast::MacroCall::parse_with_meta_path(
92                p,
93                take(&mut attributes),
94                path,
95            )?)
96        } else {
97            let mut const_token = p.parse::<Option<T![const]>>()?;
98            let mut async_token = p.parse::<Option<T![async]>>()?;
99
100            let item = match p.nth(0)? {
101                K![use] => Self::Use(ast::ItemUse::parse_with_meta(
102                    p,
103                    take(&mut attributes),
104                    take(&mut visibility),
105                )?),
106                K![enum] => Self::Enum(ast::ItemEnum::parse_with_meta(
107                    p,
108                    take(&mut attributes),
109                    take(&mut visibility),
110                )?),
111                K![struct] => Self::Struct(ast::ItemStruct::parse_with_meta(
112                    p,
113                    take(&mut attributes),
114                    take(&mut visibility),
115                )?),
116                K![impl] => Self::Impl(ast::ItemImpl::parse_with_attributes(
117                    p,
118                    take(&mut attributes),
119                )?),
120                K![fn] => Self::Fn(ast::ItemFn::parse_with_meta(
121                    p,
122                    take(&mut attributes),
123                    take(&mut visibility),
124                    take(&mut const_token),
125                    take(&mut async_token),
126                )?),
127                K![mod] => Self::Mod(ast::ItemMod::parse_with_meta(
128                    p,
129                    take(&mut attributes),
130                    take(&mut visibility),
131                )?),
132                K![ident] => {
133                    if let Some(const_token) = const_token.take() {
134                        Self::Const(ast::ItemConst::parse_with_meta(
135                            p,
136                            take(&mut attributes),
137                            take(&mut visibility),
138                            const_token,
139                        )?)
140                    } else {
141                        Self::MacroCall(p.parse()?)
142                    }
143                }
144                _ => {
145                    return Err(compile::Error::expected(
146                        p.tok_at(0)?,
147                        "`fn`, `mod`, `struct`, `enum`, `use`, or macro call",
148                    ))
149                }
150            };
151
152            if let Some(span) = const_token.option_span() {
153                return Err(compile::Error::unsupported(span, "const modifier"));
154            }
155
156            if let Some(span) = async_token.option_span() {
157                return Err(compile::Error::unsupported(span, "async modifier"));
158            }
159
160            item
161        };
162
163        if let Some(span) = attributes.option_span() {
164            return Err(compile::Error::unsupported(span, "attribute"));
165        }
166
167        if let Some(span) = visibility.option_span() {
168            return Err(compile::Error::unsupported(span, "visibility modifier"));
169        }
170
171        Ok(item)
172    }
173
174    /// Removes the first attribute in the item list and returns it if present.
175    pub(crate) fn remove_first_attribute(&mut self) -> Option<Attribute> {
176        let attributes = self.attributes_mut();
177
178        if !attributes.is_empty() {
179            return Some(attributes.remove(0));
180        }
181
182        None
183    }
184}
185
186impl Parse for Item {
187    fn parse(p: &mut Parser<'_>) -> Result<Self> {
188        let attributes = p.parse()?;
189        let visibility = p.parse()?;
190        let path = p.parse()?;
191        Self::parse_with_meta_path(p, attributes, visibility, path)
192    }
193}