Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/async/wasm/mod.rs
1pub mod executor;
2 
3use std::{
4 pin::Pin,
5 task::{Context, Poll},
6};
7 
8use futures::Future;
9pub use futures_lite::future::block_on;
10use futures_lite::FutureExt;
11use gloo::timers::future::TimeoutFuture;
12use instant::Instant;
13 
14// There is no such thing as a background thread in wasm, so all futures are local.
15pub use futures_util::future::LocalBoxFuture as BoxFuture;
16 
17// Define a trait that is implemented by all types to allow us to not
18// place any restrictions on SpawnableOutput.
19trait Unrestricted {}
20impl<T> Unrestricted for T {}
21 
22// In wasm, there's no such thing as a background thread, so all
23// futures are local. The implementation of wasm_bindgen_futures::JsFuture
24// doesn't implement Send, so we want to relax that constraint when
25// running in wasm.
26trait_set::trait_set! {
27 /// A trait representing a task which can be run in the background.
28 pub trait Spawnable = 'static + Future;
29 /// A trait representing a stream which can be polled in the background.
30 pub trait Stream = 'static + futures::Stream;
31 /// A trait representing a value which can be returned from a background
32 /// task.
33 ///
34 /// We need to supply _some_ trait bound here, so we use Unrestricted, which
35 /// doesn't apply any additional constraints on the output type.
36 pub trait SpawnableOutput = Unrestricted;
37 /// Bounds for async I/O streams passed to cross-platform networking code.
38 /// On WASM, `Send` is not required since everything runs on the main thread.
39 pub trait TransportStream = Unpin + 'static;
40}
41 
42/// A future that emits timed events.
43///
44/// This must conform to the same API as [`async_io::Timer`].
45pub struct Timer {
46 /// The actual future that will resolve at some future time,
47 /// producing the [`Instant`] at which it is configured to
48 /// be ready.
49 inner: Pin<Box<dyn Future<Output = Instant>>>,
50 /// Whether or not a [`Stream`] representation of this timer
51 /// is exhausted (and so should produce [`None`]).
52 stream_exhausted: bool,
53}
54 
55impl Timer {
56 pub fn after(duration: std::time::Duration) -> Self {
57 Self::new(duration, Instant::now() + duration)
58 }
59 
60 pub fn at(instant: Instant) -> Self {
61 let duration = instant - instant::Instant::now();
62 Self::new(duration, instant)
63 }
64 
65 pub fn never() -> Self {
66 Self {
67 inner: futures_lite::future::pending().boxed(),
68 stream_exhausted: false,
69 }
70 }
71 
72 fn new(duration: std::time::Duration, instant: Instant) -> Self {
73 let future = async move {
74 // We're never scheduling a timeout for more than 50 days, so this cast to u32 is fine.
75 TimeoutFuture::new(duration.as_millis() as u32).await;
76 instant
77 };
78 Self {
79 inner: Box::pin(future),
80 stream_exhausted: false,
81 }
82 }
83}
84 
85impl Future for Timer {
86 type Output = Instant;
87 
88 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
89 self.inner.poll(cx)
90 }
91}
92 
93impl futures::Stream for Timer {
94 type Item = Instant;
95 
96 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
97 if self.stream_exhausted {
98 return Poll::Ready(None);
99 }
100 self.inner.poll(cx).map(|val| {
101 self.stream_exhausted = true;
102 Some(val)
103 })
104 }
105}
106