rune/ast/
expr_closure.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    rt::<ast::ExprClosure>("async || 42");
7    rt::<ast::ExprClosure>("|| 42");
8    rt::<ast::ExprClosure>("|| { 42 }");
9    rt::<ast::ExprClosure>("move || { 42 }");
10    rt::<ast::ExprClosure>("async move || { 42 }");
11
12    let expr = rt::<ast::ExprClosure>("#[retry(n=3)]  || 43");
13    assert_eq!(expr.attributes.len(), 1);
14
15    let expr = rt::<ast::ExprClosure>("#[retry(n=3)] async || 43");
16    assert_eq!(expr.attributes.len(), 1);
17}
18
19/// A closure expression.
20///
21/// * `|| <expr>`.
22/// * `async || <expr>`.
23#[derive(Debug, TryClone, PartialEq, Eq, Parse, ToTokens, Spanned)]
24#[rune(parse = "meta_only")]
25#[non_exhaustive]
26pub struct ExprClosure {
27    /// The attributes for the async closure
28    #[rune(iter, meta)]
29    pub attributes: Vec<ast::Attribute>,
30    /// If the closure is async or not.
31    #[rune(iter, meta)]
32    pub async_token: Option<T![async]>,
33    /// If the closure moves data into it.
34    #[rune(iter, meta)]
35    pub move_token: Option<T![move]>,
36    /// Arguments to the closure.
37    pub args: ExprClosureArgs,
38    /// The body of the closure.
39    pub body: Box<ast::Expr>,
40    /// Opaque identifier for the closure.
41    #[rune(skip)]
42    pub(crate) id: ItemId,
43}
44
45impl ExprClosure {
46    /// Get the identifying span for this closure.
47    pub fn item_span(&self) -> Span {
48        if let Some(async_) = &self.async_token {
49            async_.span().join(self.args.span())
50        } else {
51            self.args.span()
52        }
53    }
54}
55
56expr_parse!(Closure, ExprClosure, "closure expression");
57
58/// Representation of closure arguments.
59#[derive(Debug, TryClone, PartialEq, Eq, ToTokens)]
60#[non_exhaustive]
61pub enum ExprClosureArgs {
62    /// Closure has no arguments.
63    Empty {
64        /// The `||` token.
65        token: T![||],
66    },
67    /// Closure has a list of arguments.
68    List {
69        /// The opening pipe for the argument group.
70        open: T![|],
71        /// The arguments of the function.
72        args: Vec<(ast::FnArg, Option<T![,]>)>,
73        /// The closening pipe for the argument group.
74        close: T![|],
75    },
76}
77
78impl ExprClosureArgs {
79    /// Get a slice over all arguments.
80    pub(crate) fn as_slice(&self) -> &[(ast::FnArg, Option<T![,]>)] {
81        match self {
82            Self::Empty { .. } => &[],
83            Self::List { args, .. } => &args[..],
84        }
85    }
86
87    /// Get a mutable slice over all arguments.
88    pub(crate) fn as_slice_mut(&mut self) -> &mut [(ast::FnArg, Option<T![,]>)] {
89        match self {
90            Self::Empty { .. } => &mut [],
91            Self::List { args, .. } => &mut args[..],
92        }
93    }
94}
95
96impl Parse for ExprClosureArgs {
97    fn parse(p: &mut Parser<'_>) -> Result<Self> {
98        if let Some(token) = p.parse::<Option<T![||]>>()? {
99            return Ok(ExprClosureArgs::Empty { token });
100        }
101
102        let open = p.parse()?;
103        let mut args = Vec::new();
104
105        while !p.peek::<T![|]>()? {
106            let arg = p.parse()?;
107
108            let comma = p.parse::<Option<T![,]>>()?;
109            let is_end = comma.is_none();
110            args.try_push((arg, comma))?;
111
112            if is_end {
113                break;
114            }
115        }
116
117        Ok(ExprClosureArgs::List {
118            open,
119            args,
120            close: p.parse()?,
121        })
122    }
123}
124
125impl Spanned for ExprClosureArgs {
126    fn span(&self) -> Span {
127        match self {
128            Self::Empty { token } => token.span(),
129            Self::List { open, close, .. } => open.span().join(close.span()),
130        }
131    }
132}