Skip to main content

doctest_support/
delay.rs

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