doctest_support/
delay.rs

1use std::future::Future;
2
3use crux_core::{capability::Operation, command::RequestBuilder, Command, Request};
4use serde::{Deserialize, Serialize};
5
6// ANCHOR: operation
7#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
8pub enum DelayOperation {
9    GetRandom(usize, usize),
10    Delay(usize),
11}
12// ANCHOR_END: operation
13
14// ANCHOR: output
15#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
16pub enum DelayOutput {
17    Random(usize),
18    TimeUp,
19}
20// ANCHOR_END: output
21
22// ANCHOR: operation_impl
23impl Operation for DelayOperation {
24    type Output = DelayOutput;
25}
26// ANCHOR_END: operation_impl
27
28// ANCHOR: functions
29/// Request a delay for the specified number of milliseconds.
30#[must_use]
31pub fn milliseconds<Effect, Event>(
32    millis: usize,
33) -> RequestBuilder<Effect, Event, impl Future<Output = DelayOutput>>
34where
35    Effect: Send + From<Request<DelayOperation>> + 'static,
36    Event: Send + 'static,
37{
38    Command::request_from_shell(DelayOperation::Delay(millis))
39}
40
41/// Request a delay for a random duration between `min` and `max` milliseconds.
42///
43/// # Panics
44///
45/// - will panic if `min` is greater than `max`.
46/// - will panic if the shell responds with the incorrect [`DelayOutput`] variant.
47#[must_use]
48pub fn random<Effect, Event>(
49    min: usize,
50    max: usize,
51) -> RequestBuilder<Effect, Event, impl Future<Output = DelayOutput>>
52where
53    Effect: Send + From<Request<DelayOperation>> + 'static,
54    Event: Send + 'static,
55{
56    assert!(min <= max, "min must be less than or equal to max");
57
58    Command::request_from_shell(DelayOperation::GetRandom(min, max)).then_request(|response| {
59        let DelayOutput::Random(millis) = response else {
60            panic!("Expected a random number")
61        };
62
63        Command::request_from_shell(DelayOperation::Delay(millis))
64    })
65}
66// ANCHOR_END: functions
67
68// ANCHOR: tests
69#[cfg(test)]
70mod tests {
71    use crux_core::macros::effect;
72
73    use super::*;
74
75    #[effect]
76    pub enum Effect {
77        Delay(DelayOperation),
78    }
79
80    enum Event {
81        Delay(DelayOutput),
82    }
83
84    #[test]
85    fn test_delay() {
86        let delay = 100;
87
88        let mut cmd = milliseconds(delay).then_send(Event::Delay);
89
90        let effect = cmd.expect_one_effect();
91        let Effect::Delay(mut request) = effect;
92
93        assert_eq!(request.operation, DelayOperation::Delay(delay));
94
95        request.resolve(DelayOutput::TimeUp).unwrap();
96
97        let event = cmd.events().next().unwrap();
98        let Event::Delay(output) = event;
99        assert_eq!(output, DelayOutput::TimeUp);
100
101        assert!(cmd.is_done());
102    }
103
104    #[test]
105    fn test_random() {
106        let min = 100;
107        let max = 200;
108
109        let mut cmd = random(min, max).then_send(Event::Delay);
110
111        let effect = cmd.expect_one_effect();
112        let Effect::Delay(mut request) = effect;
113
114        assert_eq!(request.operation, DelayOperation::GetRandom(min, max));
115        request.resolve(DelayOutput::Random(150)).unwrap();
116
117        let effect = cmd.expect_one_effect();
118        let Effect::Delay(mut request) = effect;
119
120        assert_eq!(request.operation, DelayOperation::Delay(150));
121
122        request.resolve(DelayOutput::TimeUp).unwrap();
123
124        let event = cmd.events().next().unwrap();
125        let Event::Delay(output) = event;
126        assert_eq!(output, DelayOutput::TimeUp);
127
128        assert!(cmd.is_done());
129    }
130}
131// ANCHOR_END: tests