ocpp_client/runtime.rs
1//! Runtime abstraction so the engine (`Client<E>`) doesn't hard-depend on tokio. `Executor`
2//! spawns the background read loop and per-handler tasks; `Timer` drives request/ping
3//! timeouts. Both are dyn-safe (boxed-future style) so `Client<E>` stays generic over one
4//! type parameter only, the same way `TransportSink`/`TransportStream` are already boxed
5//! instead of threaded through as generics. `tokio-runtime` (see `runtime::tokio`) provides
6//! the default std impls; embedded users supply their own (e.g. backed by
7//! `embassy-executor`/`embassy-time`).
8
9use alloc::boxed::Box;
10use core::future::Future;
11use core::pin::Pin;
12use core::task::Poll;
13use core::time::Duration;
14
15#[cfg(feature = "tokio-runtime")]
16pub mod tokio;
17
18/// Spawns futures onto a background executor. Implementations must actually run the future
19/// to completion independently of the caller awaiting anything - `Client::from_transport`'s
20/// read loop, `on()`'s per-action handler loop, and `on_ping()`'s subscriber loop all rely on
21/// `spawn` to keep running in the background.
22pub trait Executor: Send + Sync + 'static {
23 fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
24}
25
26/// Produces timer delays. `with_timeout` (below) is built on top of this single dyn-safe
27/// method rather than a generic `timeout<F>` method, so `Timer` itself stays object-safe.
28pub trait Timer: Send + Sync + 'static {
29 fn delay<'a>(&'a self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
30}
31
32/// Returned by [`with_timeout`] when `duration` elapses before `fut` resolves.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct Elapsed;
35
36/// Race `fut` against `timer.delay(duration)`, by hand - no `futures::select`/extra
37/// dependency needed, just polling both each wake via `core::future::poll_fn`.
38pub(crate) async fn with_timeout<F: Future>(
39 timer: &dyn Timer,
40 duration: Duration,
41 fut: F,
42) -> Result<F::Output, Elapsed> {
43 let mut fut = core::pin::pin!(fut);
44 let mut delay = timer.delay(duration);
45 core::future::poll_fn(move |cx| {
46 if let Poll::Ready(value) = fut.as_mut().poll(cx) {
47 return Poll::Ready(Ok(value));
48 }
49 if let Poll::Ready(()) = delay.as_mut().poll(cx) {
50 return Poll::Ready(Err(Elapsed));
51 }
52 Poll::Pending
53 })
54 .await
55}