Skip to main content

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