1use std::future::Future;
2
3use crux_core::{capability::Operation, command::RequestBuilder, Command, Request};
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
8pub enum DelayOperation {
9    GetRandom(usize, usize),
10    Delay(usize),
11}
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
16pub enum DelayOutput {
17    Random(usize),
18    TimeUp,
19}
20impl Operation for DelayOperation {
24    type Output = DelayOutput;
25}
26#[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#[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#[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