rune/ast/
expr_select.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    let select = rt::<ast::ExprSelect>(
7        r#"
8    select {
9        _ = a => 0,
10        _ = b => {},
11        _ = c => {}
12        default => ()
13    }
14    "#,
15    );
16
17    assert_eq!(4, select.branches.len());
18    assert!(matches!(
19        select.branches.get(1),
20        Some(&(ast::ExprSelectBranch::Pat(..), Some(..)))
21    ));
22    assert!(matches!(
23        select.branches.get(2),
24        Some(&(ast::ExprSelectBranch::Pat(..), None))
25    ));
26    assert!(matches!(
27        select.branches.get(3),
28        Some(&(ast::ExprSelectBranch::Default(..), None))
29    ));
30}
31
32/// A `select` expression that selects over a collection of futures.
33///
34/// * `select { [arm]* }`.
35#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
36#[non_exhaustive]
37pub struct ExprSelect {
38    /// The attributes of the `select`
39    #[rune(iter)]
40    pub attributes: Vec<ast::Attribute>,
41    /// The `select` keyword.
42    pub select: T![select],
43    /// The open brace.
44    pub open: T!['{'],
45    /// The branches of the select.
46    #[rune(iter)]
47    pub branches: Vec<(ExprSelectBranch, Option<T![,]>)>,
48    /// The close brace.
49    pub close: T!['}'],
50}
51
52impl ExprSelect {
53    /// Parse the `select` expression and attach the given attributes
54    pub(crate) fn parse_with_attributes(
55        p: &mut Parser<'_>,
56        attributes: Vec<ast::Attribute>,
57    ) -> Result<Self> {
58        let select = p.parse()?;
59        let open = p.parse()?;
60
61        let mut branches = Vec::new();
62
63        while !p.peek::<T!['}']>()? {
64            let branch = ExprSelectBranch::parse(p)?;
65            let comma = p.parse::<Option<T![,]>>()?;
66            let is_end = ast::utils::is_block_end(branch.expr(), comma.as_ref());
67            branches.try_push((branch, comma))?;
68
69            if is_end {
70                break;
71            }
72        }
73
74        let close = p.parse()?;
75
76        Ok(Self {
77            attributes,
78            select,
79            open,
80            branches,
81            close,
82        })
83    }
84}
85
86expr_parse!(Select, ExprSelect, "select expression");
87
88/// A single selection branch.
89#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
90#[non_exhaustive]
91#[allow(clippy::large_enum_variant)]
92pub enum ExprSelectBranch {
93    /// A patterned branch.
94    Pat(ExprSelectPatBranch),
95    /// A default branch.
96    Default(ExprDefaultBranch),
97}
98
99impl ExprSelectBranch {
100    /// Access the expression body.
101    pub(crate) fn expr(&self) -> &ast::Expr {
102        match self {
103            ExprSelectBranch::Pat(pat) => &pat.body,
104            ExprSelectBranch::Default(def) => &def.body,
105        }
106    }
107}
108
109impl Parse for ExprSelectBranch {
110    fn parse(p: &mut Parser<'_>) -> Result<Self> {
111        Ok(if p.peek::<T![default]>()? {
112            Self::Default(p.parse()?)
113        } else {
114            Self::Pat(p.parse()?)
115        })
116    }
117}
118
119/// A single selection branch.
120#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Parse, Spanned)]
121#[non_exhaustive]
122pub struct ExprSelectPatBranch {
123    /// The identifier to bind the result to.
124    pub pat: ast::Pat,
125    /// `=`.
126    pub eq: T![=],
127    /// The expression that should evaluate to a future.
128    pub expr: ast::Expr,
129    /// `=>`.
130    pub rocket: T![=>],
131    /// The body of the expression.
132    pub body: ast::Expr,
133}
134
135/// A single selection branch.
136#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Parse, Spanned)]
137#[non_exhaustive]
138pub struct ExprDefaultBranch {
139    /// The `default` keyword.
140    pub default: T![default],
141    /// `=>`.
142    pub rocket: T![=>],
143    /// The body of the expression.
144    pub body: ast::Expr,
145}