1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#[macro_export]
/// Selects over a range of possible future events, processing exactly one.
/// Inspired by equivalent behaviours in other programming languages such as Go
/// and Kotlin, and ultimately the `select` system call from POSIX.
///
/// Which event gets processed is a case of bounded non-determinism: the
/// implementation makes no guarantee about which event gets processed if
/// multiple become possible around the same time, only that it will process one
/// of them if at least one can be processed.
///
/// # Examples
///
/// ```
/// fn foo(ctx: Context) {
///     let mut x = 0;
///     let mut l = Loop::new(Duration::from_secs(1));
///     loop {
///         println!("x = {}", x);
///         x += 1;
///         select! {
///             _ = l.next() => continue,
///             _ = ctx.done() => break,
///         }
///     }
/// }
/// ```
macro_rules! select {
    { $( $var:pat = $event:expr $(; $sub:pat = $dep:expr)* => $body:expr ),+ $(,)? } => {{
        let mut events = $crate::select_head!($($event $(; $sub = $dep)* ;;)+);
        $crate::select_body!{loop {
            $crate::rtos::GenericSleep::sleep($crate::select_sleep!(events; $($event,)+));
            events = $crate::select_match!{events; |r| r; $($event,)+};
        }; $($var => {$body},)+}
    }};
}

#[macro_export]
#[doc(hidden)]
macro_rules! select_head {
    ($event:expr $(; $sub:pat = $dep:expr)* ;;) => {
        $crate::select_init!($event $(; $sub = $dep)*)
    };
    ($event:expr $(; $sub1:pat = $dep1:expr)* ;; $($rest:expr $(; $sub:pat = $dep:expr)* ;;)+) => {
        ($crate::select_init!($event $(; $sub1 = $dep1)*), $crate::select_head!($($rest $(; $sub = $dep)* ;;)*))
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! select_init {
    ($(@NEXT)? $event:expr) => {
        $event
    };
    ($event:expr $(; $sub:pat = $dep:expr)+) => {
        $crate::rtos::select_option($crate::select_init!(@NEXT ::core::option::Option::Some($event) $(; $sub = $dep)+))
    };
    (@NEXT $event:expr; $sub1:pat = $dep1:expr $(; $sub:pat = $dep:expr)*) => {
        $crate::select_init!(@NEXT if let $sub1 = $dep1 { $event } else { ::core::option::Option::None } $(; $sub = $dep)*)
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! select_match {
    { $event:expr; $cons:expr; $_:expr, } => {
        match $crate::rtos::Selectable::poll($event) {
            ::core::result::Result::Ok(r) => break $cons(r),
            ::core::result::Result::Err(s) => s,
        }
    };
    { $events:expr; $cons:expr; $_:expr, $($rest:expr,)+ } => {
        match $crate::rtos::Selectable::poll($events.0) {
            ::core::result::Result::Ok(r) => break $cons(::core::result::Result::Ok(r)),
            ::core::result::Result::Err(s) => (
                s,
                $crate::select_match!{$events.1; |r| $cons(::core::result::Result::Err(r)); $($rest,)*}
            ),
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! select_body {
    { $result:expr; $var:pat => $body:expr, } => {
        match $result {
            $var => $body,
        }
    };
    { $result:expr; $var:pat => $body:expr, $($vars:pat => $bodys:expr,)+ } => {
        match $result {
            ::core::result::Result::Ok($var) => $body,
            ::core::result::Result::Err(r) => $crate::select_body!{r; $($vars => $bodys,)*},
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! select_sleep {
    ($event:expr; $_:expr,) => {$crate::rtos::Selectable::sleep(&$event)};
    ($events:expr; $_:expr, $($rest:expr,)+) => {
        $crate::rtos::Selectable::sleep(&$events.0).combine($crate::select_sleep!($events.1; $($rest,)+))
    };
}

#[macro_export]
/// Generates a future event (i.e. one which implements
/// [`crate::rtos::Selectable`]) from a similar recipe as the [`select!`]
/// macro, combining the behaviour of [`select_map`](crate::rtos::select_map)
/// and [`select_any!`](crate::select_any!).
///
/// There is one important difference to note between this macro and
/// [`select!`]: since this macro needs to generate an object containing the
/// event processing recipe, the body expressions are placed inside lambdas, and
/// therefore contextual expressions such as `break`, `continue` and `return`
/// are not valid.
macro_rules! select_merge {
    { $( $var:pat = $event:expr $(; $sub:pat = $dep:expr)* => $body:expr ),+ $(,)? } => {{
        #[allow(clippy::redundant_closure)]
        let r = $crate::select_any!($($crate::rtos::select_map($event, |$var| $body) $(; $sub = $dep)*),+);
        r
    }};
}

#[macro_export]
/// Generates a future event (i.e. one which implements
/// [`crate::rtos::Selectable`]) from a set of events which all have the same
/// result type, by repeated application of [`crate::rtos::select_either`].
macro_rules! select_any {
    ($event:expr $(; $sub:pat = $dep:expr)* $(,)?) => {
        $crate::select_init!($event $(; $sub = $dep)*)
    };
    ($event:expr $(; $sub1:pat = $dep1:expr)*, $($rest:expr $(; $sub:pat = $dep:expr)*),+ $(,)?) => {
        $crate::rtos::select_either($crate::select_init!($event $(; $sub1 = $dep1)*), $crate::select_any!($($rest $(; $sub = $dep)*),+))
    };
}