Skip to main content

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    /// Borrow as an identifier used for field access calls.
37    ///
38    /// This is only allowed if there are no other path components
39    /// and the path segment is not `Crate` or `Super`.
40    pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
41        if self.rest.is_empty() && self.trailing.is_none() && self.global.is_none() {
42            self.first.try_as_ident()
43        } else {
44            None
45        }
46    }
47}
48
49impl Peek for Path {
50    fn peek(p: &mut Peeker<'_>) -> bool {
51        matches!(p.nth(0), K![::]) || PathSegment::peek(p)
52    }
53}
54
55impl IntoExpectation for &Path {
56    fn into_expectation(self) -> Expectation {
57        Expectation::Description("path")
58    }
59}
60
61/// Resolve implementation for path which "stringifies" it.
62impl Resolve<'_> for Path {
63    type Output = Box<str>;
64
65    fn resolve(&self, cx: ResolveContext<'_, '_>) -> Result<Self::Output> {
66        let mut buf = String::new();
67
68        if self.global.is_some() {
69            buf.try_push_str("::")?;
70        }
71
72        match &self.first {
73            PathSegment::SelfType(_) => {
74                buf.try_push_str("Self")?;
75            }
76            PathSegment::SelfValue(_) => {
77                buf.try_push_str("self")?;
78            }
79            PathSegment::Ident(ident) => {
80                buf.try_push_str(ident.resolve(cx)?)?;
81            }
82            PathSegment::Crate(_) => {
83                buf.try_push_str("crate")?;
84            }
85            PathSegment::Super(_) => {
86                buf.try_push_str("super")?;
87            }
88            PathSegment::Generics(_) => {
89                buf.try_push_str("<*>")?;
90            }
91        }
92
93        for (_, segment) in &self.rest {
94            buf.try_push_str("::")?;
95
96            match segment {
97                PathSegment::SelfType(_) => {
98                    buf.try_push_str("Self")?;
99                }
100                PathSegment::SelfValue(_) => {
101                    buf.try_push_str("self")?;
102                }
103                PathSegment::Ident(ident) => {
104                    buf.try_push_str(ident.resolve(cx)?)?;
105                }
106                PathSegment::Crate(_) => {
107                    buf.try_push_str("crate")?;
108                }
109                PathSegment::Super(_) => {
110                    buf.try_push_str("super")?;
111                }
112                PathSegment::Generics(_) => {
113                    buf.try_push_str("<*>")?;
114                }
115            }
116        }
117
118        if self.trailing.is_some() {
119            buf.try_push_str("::")?;
120        }
121
122        Ok(buf.try_into_boxed_str()?)
123    }
124}
125
126/// An identified path kind.
127#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq)]
128#[try_clone(copy)]
129#[non_exhaustive]
130pub enum PathKind<'a> {
131    /// A path that is the `self` value.
132    SelfValue,
133    /// A path that is the identifier.
134    Ident(&'a ast::Ident),
135}
136
137/// Part of a `::` separated path.
138#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
139#[non_exhaustive]
140pub enum PathSegment {
141    /// A path segment that contains `Self`.
142    SelfType(T![Self]),
143    /// A path segment that contains `self`.
144    SelfValue(T![self]),
145    /// A path segment that is an identifier.
146    Ident(ast::Ident),
147    /// The `crate` keyword used as a path segment.
148    Crate(T![crate]),
149    /// The `super` keyword use as a path segment.
150    Super(T![super]),
151    /// A path segment that is a generic argument.
152    Generics(ast::AngleBracketed<PathSegmentExpr, T![,]>),
153}
154
155impl PathSegment {
156    /// Borrow as an identifier.
157    ///
158    /// This is only allowed if the PathSegment is `Ident(_)`
159    /// and not `Crate` or `Super`.
160    pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
161        if let PathSegment::Ident(ident) = self {
162            Some(ident)
163        } else {
164            None
165        }
166    }
167}
168
169impl IntoExpectation for PathSegment {
170    fn into_expectation(self) -> Expectation {
171        Expectation::Description("path segment")
172    }
173}
174
175impl Parse for PathSegment {
176    fn parse(p: &mut Parser<'_>) -> Result<Self> {
177        let segment = match p.nth(0)? {
178            K![Self] => Self::SelfType(p.parse()?),
179            K![self] => Self::SelfValue(p.parse()?),
180            K![ident] => Self::Ident(p.parse()?),
181            K![crate] => Self::Crate(p.parse()?),
182            K![super] => Self::Super(p.parse()?),
183            K![<] => Self::Generics(p.parse()?),
184            _ => {
185                return Err(compile::Error::expected(p.tok_at(0)?, "path segment"));
186            }
187        };
188
189        Ok(segment)
190    }
191}
192
193impl Peek for PathSegment {
194    fn peek(p: &mut Peeker<'_>) -> bool {
195        matches!(
196            p.nth(0),
197            K![<] | K![Self] | K![self] | K![crate] | K![super] | K![ident]
198        )
199    }
200}
201
202/// Used to parse an expression without supporting an immediate binary expression.
203#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
204#[non_exhaustive]
205pub struct PathSegmentExpr {
206    /// The expression that makes up the path segment.
207    pub expr: ast::Expr,
208}
209
210impl Parse for PathSegmentExpr {
211    fn parse(p: &mut Parser<'_>) -> Result<Self> {
212        let expr = ast::Expr::parse_with(
213            p,
214            ast::expr::NOT_EAGER_BRACE,
215            ast::expr::NOT_EAGER_BINARY,
216            ast::expr::NOT_CALLABLE,
217        )?;
218
219        Ok(Self { expr })
220    }
221}
222
223impl Peek for PathSegmentExpr {
224    fn peek(p: &mut Peeker<'_>) -> bool {
225        ast::Expr::peek(p)
226    }
227}