rune/ast/
path.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    rt::<ast::Path>("foo::bar");
7    rt::<ast::Path>("Self::bar");
8    rt::<ast::Path>("self::bar");
9    rt::<ast::Path>("crate::bar");
10    rt::<ast::Path>("super::bar");
11    rt::<ast::Path>("HashMap::<Foo, Bar>");
12    rt::<ast::Path>("super::HashMap::<Foo, Bar>");
13}
14
15/// A path, where each element is separated by a `::`.
16#[derive(Debug, TryClone, PartialEq, Eq, Parse, ToTokens, Spanned)]
17#[non_exhaustive]
18pub struct Path {
19    /// The optional leading colon `::` indicating global scope.
20    #[rune(iter)]
21    pub global: Option<T![::]>,
22    /// The first component in the path.
23    pub first: PathSegment,
24    /// The rest of the components in the path.
25    #[rune(iter)]
26    pub rest: Vec<(T![::], PathSegment)>,
27    /// Trailing scope.
28    #[rune(iter)]
29    pub trailing: Option<T![::]>,
30    /// Opaque id associated with path.
31    #[rune(skip)]
32    pub(crate) id: ItemId,
33}
34
35impl Path {
36    /// Identify the kind of the path.
37    pub(crate) fn as_kind(&self) -> Option<PathKind<'_>> {
38        if self.rest.is_empty() && self.trailing.is_none() && self.global.is_none() {
39            match &self.first {
40                PathSegment::SelfValue(..) => Some(PathKind::SelfValue),
41                PathSegment::Ident(ident) => Some(PathKind::Ident(ident)),
42                _ => None,
43            }
44        } else {
45            None
46        }
47    }
48
49    /// Borrow as an identifier used for field access calls.
50    ///
51    /// This is only allowed if there are no other path components
52    /// and the path segment is not `Crate` or `Super`.
53    pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
54        if self.rest.is_empty() && self.trailing.is_none() && self.global.is_none() {
55            self.first.try_as_ident()
56        } else {
57            None
58        }
59    }
60
61    /// Borrow ident and generics at the same time.
62    pub(crate) fn try_as_ident_generics(
63        &self,
64    ) -> Option<(
65        &ast::Ident,
66        Option<&ast::AngleBracketed<PathSegmentExpr, T![,]>>,
67    )> {
68        if self.trailing.is_none() && self.global.is_none() {
69            if let Some(ident) = self.first.try_as_ident() {
70                let generics = if let [(_, ast::PathSegment::Generics(generics))] = &self.rest[..] {
71                    Some(generics)
72                } else {
73                    None
74                };
75
76                return Some((ident, generics));
77            }
78        }
79
80        None
81    }
82}
83
84impl Peek for Path {
85    fn peek(p: &mut Peeker<'_>) -> bool {
86        matches!(p.nth(0), K![::]) || PathSegment::peek(p)
87    }
88}
89
90impl IntoExpectation for &Path {
91    fn into_expectation(self) -> Expectation {
92        Expectation::Description("path")
93    }
94}
95
96/// Resolve implementation for path which "stringifies" it.
97impl Resolve<'_> for Path {
98    type Output = Box<str>;
99
100    fn resolve(&self, cx: ResolveContext<'_>) -> Result<Self::Output> {
101        let mut buf = String::new();
102
103        if self.global.is_some() {
104            buf.try_push_str("::")?;
105        }
106
107        match &self.first {
108            PathSegment::SelfType(_) => {
109                buf.try_push_str("Self")?;
110            }
111            PathSegment::SelfValue(_) => {
112                buf.try_push_str("self")?;
113            }
114            PathSegment::Ident(ident) => {
115                buf.try_push_str(ident.resolve(cx)?)?;
116            }
117            PathSegment::Crate(_) => {
118                buf.try_push_str("crate")?;
119            }
120            PathSegment::Super(_) => {
121                buf.try_push_str("super")?;
122            }
123            PathSegment::Generics(_) => {
124                buf.try_push_str("<*>")?;
125            }
126        }
127
128        for (_, segment) in &self.rest {
129            buf.try_push_str("::")?;
130
131            match segment {
132                PathSegment::SelfType(_) => {
133                    buf.try_push_str("Self")?;
134                }
135                PathSegment::SelfValue(_) => {
136                    buf.try_push_str("self")?;
137                }
138                PathSegment::Ident(ident) => {
139                    buf.try_push_str(ident.resolve(cx)?)?;
140                }
141                PathSegment::Crate(_) => {
142                    buf.try_push_str("crate")?;
143                }
144                PathSegment::Super(_) => {
145                    buf.try_push_str("super")?;
146                }
147                PathSegment::Generics(_) => {
148                    buf.try_push_str("<*>")?;
149                }
150            }
151        }
152
153        if self.trailing.is_some() {
154            buf.try_push_str("::")?;
155        }
156
157        Ok(buf.try_into_boxed_str()?)
158    }
159}
160
161/// An identified path kind.
162#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq)]
163#[try_clone(copy)]
164#[non_exhaustive]
165pub enum PathKind<'a> {
166    /// A path that is the `self` value.
167    SelfValue,
168    /// A path that is the identifier.
169    Ident(&'a ast::Ident),
170}
171
172/// Part of a `::` separated path.
173#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
174#[non_exhaustive]
175pub enum PathSegment {
176    /// A path segment that contains `Self`.
177    SelfType(T![Self]),
178    /// A path segment that contains `self`.
179    SelfValue(T![self]),
180    /// A path segment that is an identifier.
181    Ident(ast::Ident),
182    /// The `crate` keyword used as a path segment.
183    Crate(T![crate]),
184    /// The `super` keyword use as a path segment.
185    Super(T![super]),
186    /// A path segment that is a generic argument.
187    Generics(ast::AngleBracketed<PathSegmentExpr, T![,]>),
188}
189
190impl PathSegment {
191    /// Borrow as an identifier.
192    ///
193    /// This is only allowed if the PathSegment is `Ident(_)`
194    /// and not `Crate` or `Super`.
195    pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
196        if let PathSegment::Ident(ident) = self {
197            Some(ident)
198        } else {
199            None
200        }
201    }
202}
203
204impl IntoExpectation for PathSegment {
205    fn into_expectation(self) -> Expectation {
206        Expectation::Description("path segment")
207    }
208}
209
210impl Parse for PathSegment {
211    fn parse(p: &mut Parser<'_>) -> Result<Self> {
212        let segment = match p.nth(0)? {
213            K![Self] => Self::SelfType(p.parse()?),
214            K![self] => Self::SelfValue(p.parse()?),
215            K![ident] => Self::Ident(p.parse()?),
216            K![crate] => Self::Crate(p.parse()?),
217            K![super] => Self::Super(p.parse()?),
218            K![<] => Self::Generics(p.parse()?),
219            _ => {
220                return Err(compile::Error::expected(p.tok_at(0)?, "path segment"));
221            }
222        };
223
224        Ok(segment)
225    }
226}
227
228impl Peek for PathSegment {
229    fn peek(p: &mut Peeker<'_>) -> bool {
230        matches!(
231            p.nth(0),
232            K![<] | K![Self] | K![self] | K![crate] | K![super] | K![ident]
233        )
234    }
235}
236
237/// Used to parse an expression without supporting an immediate binary expression.
238#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
239#[non_exhaustive]
240pub struct PathSegmentExpr {
241    /// The expression that makes up the path segment.
242    pub expr: ast::Expr,
243}
244
245impl Parse for PathSegmentExpr {
246    fn parse(p: &mut Parser<'_>) -> Result<Self> {
247        let expr = ast::Expr::parse_with(
248            p,
249            ast::expr::NOT_EAGER_BRACE,
250            ast::expr::NOT_EAGER_BINARY,
251            ast::expr::NOT_CALLABLE,
252        )?;
253
254        Ok(Self { expr })
255    }
256}
257
258impl Peek for PathSegmentExpr {
259    fn peek(p: &mut Peeker<'_>) -> bool {
260        ast::Expr::peek(p)
261    }
262}