ping/
ping.rs

1//! In this example I'm showing off a few different things.
2//! - Singletons, with [`acril_rt::Context::singleton`];
3//! - Non-void responses, with [`GetOutput`]
4//! - "Dependency injection", if you can call it that, based on `#[cfg(debug_assertions)]` with
5//! [`Printer`]
6
7// we're toggling between stdout and string based on debug_assertions, so one of them is dead code
8// in any case
9#[allow(dead_code)]
10enum Printer {
11    ToString(String),
12    ToStdout,
13}
14
15impl acril::Service for Printer {
16    type Context = acril_rt::Context<Self>;
17    type Error = ();
18}
19
20struct GetOutput;
21
22impl acril::Handler<String> for Printer {
23    type Response = ();
24    async fn call(
25        &mut self,
26        request: String,
27        _cx: &mut Self::Context,
28    ) -> Result<Self::Response, Self::Error> {
29        match self {
30            Self::ToStdout => println!("{request}"),
31            Self::ToString(s) => s.push_str(&request),
32        };
33        Ok(())
34    }
35}
36
37impl acril::Handler<GetOutput> for Printer {
38    type Response = Option<String>;
39    async fn call(
40        &mut self,
41        _get_string: GetOutput,
42        _cx: &mut Self::Context,
43    ) -> Result<Self::Response, Self::Error> {
44        if let Self::ToString(ref s) = self {
45            Ok(Some(s.to_owned()))
46        } else {
47            Ok(None)
48        }
49    }
50}
51
52struct Pinger {
53    count: u32,
54}
55
56struct Ping;
57struct Pong(u32);
58
59impl acril::Service for Pinger {
60    type Context = acril_rt::Context<Self>;
61    type Error = ();
62}
63impl acril::Handler<Ping> for Pinger {
64    type Response = Pong;
65
66    async fn call(&mut self, _ping: Ping, cx: &mut Self::Context) -> Result<Pong, Self::Error> {
67        cx.singleton::<Printer>()
68            .await
69            .send(format!("ping #{}", self.count))
70            .await?;
71
72        self.count += 1;
73
74        Ok(Pong(self.count))
75    }
76}
77impl acril::Handler<GetOutput> for Pinger {
78    type Response = u32;
79    async fn call(
80        &mut self,
81        _request: GetOutput,
82        _cx: &mut Self::Context,
83    ) -> Result<Self::Response, Self::Error> {
84        Ok(self.count)
85    }
86}
87
88#[tokio::main(flavor = "current_thread")]
89async fn main() {
90    // make sure the runtime can spawn !Send tasks
91    let local_set = tokio::task::LocalSet::new();
92    let _guard = local_set.enter();
93
94    let runtime = acril_rt::Runtime::new();
95
96    local_set.run_until(async move {
97        #[cfg(debug_assertions)]
98        let printer = runtime.spawn(Printer::ToStdout).await;
99        #[cfg(not(debug_assertions))]
100        let printer = runtime.spawn(Printer::ToString(String::new())).await;
101        let pinger = runtime.spawn(Pinger { count: 0 }).await;
102
103        printer.send("Liftoff!".to_string()).await.unwrap();
104
105        for i in 0..10000 {
106            let pong = pinger.send(Ping).await.unwrap();
107
108            // i is 0-based and pong is 1-based because the pinger increments before returning
109            assert_eq!(pong.0, i + 1);
110
111            printer.send(format!("pong #{}", pong.0)).await.unwrap();
112        }
113
114        assert_eq!(pinger.send(GetOutput).await.unwrap(), 10000);
115        #[cfg(not(debug_assertions))]
116        // assert that the output is Ok(Some(non_empty_string))
117        assert!(!printer.send(GetOutput).await.unwrap().unwrap().is_empty());
118
119        printer.send("We're done!".to_string()).await.unwrap();
120    }).await;
121}