2020-01-20 04:47:36 +01:00
|
|
|
//! Run commands and keep track of subscriptions.
|
2022-01-28 17:35:47 +07:00
|
|
|
use crate::subscription;
|
2025-04-02 10:39:27 +02:00
|
|
|
use crate::{BoxStream, Executor, MaybeSend};
|
2020-01-19 09:06:48 +01:00
|
|
|
|
2025-05-31 05:50:25 +02:00
|
|
|
use futures::{Sink, SinkExt, channel::mpsc};
|
2020-01-19 09:06:48 +01:00
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
|
2020-01-20 09:49:17 +01:00
|
|
|
/// A batteries-included runtime of commands and subscriptions.
|
|
|
|
|
///
|
|
|
|
|
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
|
2023-09-09 12:24:47 +02:00
|
|
|
/// `Command` or [`Subscription`] and get notified of the results!
|
2022-04-30 14:20:52 +02:00
|
|
|
///
|
2023-09-09 12:24:47 +02:00
|
|
|
/// [`Subscription`]: crate::Subscription
|
2020-01-19 09:06:48 +01:00
|
|
|
#[derive(Debug)]
|
2023-03-05 04:15:10 +01:00
|
|
|
pub struct Runtime<Executor, Sender, Message> {
|
2020-01-19 09:06:48 +01:00
|
|
|
executor: Executor,
|
2020-01-19 11:08:32 +01:00
|
|
|
sender: Sender,
|
2023-03-05 04:15:10 +01:00
|
|
|
subscriptions: subscription::Tracker,
|
2020-01-19 09:06:48 +01:00
|
|
|
_message: PhantomData<Message>,
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 04:15:10 +01:00
|
|
|
impl<Executor, Sender, Message> Runtime<Executor, Sender, Message>
|
2020-01-19 09:06:48 +01:00
|
|
|
where
|
|
|
|
|
Executor: self::Executor,
|
2022-01-28 17:35:47 +07:00
|
|
|
Sender: Sink<Message, Error = mpsc::SendError>
|
|
|
|
|
+ Unpin
|
|
|
|
|
+ MaybeSend
|
|
|
|
|
+ Clone
|
|
|
|
|
+ 'static,
|
|
|
|
|
Message: MaybeSend + 'static,
|
2020-01-19 09:06:48 +01:00
|
|
|
{
|
2020-01-20 09:49:17 +01:00
|
|
|
/// Creates a new empty [`Runtime`].
|
|
|
|
|
///
|
|
|
|
|
/// You need to provide:
|
|
|
|
|
/// - an [`Executor`] to spawn futures
|
|
|
|
|
/// - a `Sender` implementing `Sink` to receive the results
|
2020-01-19 11:08:32 +01:00
|
|
|
pub fn new(executor: Executor, sender: Sender) -> Self {
|
2020-01-19 09:06:48 +01:00
|
|
|
Self {
|
2020-01-19 10:17:08 +01:00
|
|
|
executor,
|
2020-01-19 11:08:32 +01:00
|
|
|
sender,
|
2020-01-19 10:17:08 +01:00
|
|
|
subscriptions: subscription::Tracker::new(),
|
2020-01-19 09:06:48 +01:00
|
|
|
_message: PhantomData,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-20 09:49:17 +01:00
|
|
|
/// Runs the given closure inside the [`Executor`] of the [`Runtime`].
|
|
|
|
|
///
|
|
|
|
|
/// See [`Executor::enter`] to learn more.
|
2020-01-19 10:17:08 +01:00
|
|
|
pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
|
|
|
|
|
self.executor.enter(f)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-02 20:33:22 +02:00
|
|
|
/// Runs a future to completion in the current thread within the [`Runtime`].
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
|
pub fn block_on<T>(&mut self, future: impl Future<Output = T>) -> T {
|
|
|
|
|
self.executor.block_on(future)
|
2020-01-19 09:06:48 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 00:12:48 +01:00
|
|
|
/// Runs a [`Stream`] in the [`Runtime`] until completion.
|
|
|
|
|
///
|
|
|
|
|
/// The resulting `Message`s will be forwarded to the `Sender` of the
|
|
|
|
|
/// [`Runtime`].
|
|
|
|
|
///
|
|
|
|
|
/// [`Stream`]: BoxStream
|
|
|
|
|
pub fn run(&mut self, stream: BoxStream<Message>) {
|
|
|
|
|
use futures::{FutureExt, StreamExt};
|
|
|
|
|
|
|
|
|
|
let sender = self.sender.clone();
|
|
|
|
|
let future =
|
|
|
|
|
stream.map(Ok).forward(sender).map(|result| match result {
|
|
|
|
|
Ok(()) => (),
|
|
|
|
|
Err(error) => {
|
|
|
|
|
log::warn!(
|
|
|
|
|
"Stream could not run until completion: {error}"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.executor.spawn(future);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-31 05:50:25 +02:00
|
|
|
/// Sends a message concurrently through the [`Runtime`].
|
|
|
|
|
pub fn send(&mut self, message: Message) {
|
|
|
|
|
let mut sender = self.sender.clone();
|
|
|
|
|
|
|
|
|
|
self.executor.spawn(async move {
|
|
|
|
|
let _ = sender.send(message).await;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-20 09:49:17 +01:00
|
|
|
/// Tracks a [`Subscription`] in the [`Runtime`].
|
|
|
|
|
///
|
|
|
|
|
/// It will spawn new streams or close old ones as necessary! See
|
|
|
|
|
/// [`Tracker::update`] to learn more about this!
|
|
|
|
|
///
|
2020-11-25 07:11:27 +01:00
|
|
|
/// [`Tracker::update`]: subscription::Tracker::update
|
2023-09-09 12:24:47 +02:00
|
|
|
/// [`Subscription`]: crate::Subscription
|
2020-01-19 09:06:48 +01:00
|
|
|
pub fn track(
|
|
|
|
|
&mut self,
|
2023-03-05 04:15:10 +01:00
|
|
|
recipes: impl IntoIterator<
|
|
|
|
|
Item = Box<dyn subscription::Recipe<Output = Message>>,
|
|
|
|
|
>,
|
2020-01-19 09:06:48 +01:00
|
|
|
) {
|
2020-02-16 11:31:37 +01:00
|
|
|
let Runtime {
|
|
|
|
|
executor,
|
|
|
|
|
subscriptions,
|
|
|
|
|
sender,
|
|
|
|
|
..
|
|
|
|
|
} = self;
|
|
|
|
|
|
2023-03-05 04:15:10 +01:00
|
|
|
let futures = executor.enter(|| {
|
|
|
|
|
subscriptions.update(recipes.into_iter(), sender.clone())
|
|
|
|
|
});
|
2020-01-19 09:06:48 +01:00
|
|
|
|
|
|
|
|
for future in futures {
|
2020-02-16 11:31:37 +01:00
|
|
|
executor.spawn(future);
|
2020-01-19 09:06:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-20 09:49:17 +01:00
|
|
|
/// Broadcasts an event to all the subscriptions currently alive in the
|
|
|
|
|
/// [`Runtime`].
|
|
|
|
|
///
|
|
|
|
|
/// See [`Tracker::broadcast`] to learn more.
|
|
|
|
|
///
|
2020-11-25 07:11:27 +01:00
|
|
|
/// [`Tracker::broadcast`]: subscription::Tracker::broadcast
|
2024-06-11 19:41:05 +02:00
|
|
|
pub fn broadcast(&mut self, event: subscription::Event) {
|
|
|
|
|
self.subscriptions.broadcast(event);
|
2020-01-19 09:06:48 +01:00
|
|
|
}
|
|
|
|
|
}
|