iced_futures/
runtime.rs

1//! Run commands and keep track of subscriptions.
2use crate::subscription;
3use crate::{BoxFuture, BoxStream, Executor, MaybeSend};
4
5use futures::{Sink, channel::mpsc};
6use std::marker::PhantomData;
7
8/// A batteries-included runtime of commands and subscriptions.
9///
10/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
11/// `Command` or [`Subscription`] and get notified of the results!
12///
13/// [`Subscription`]: crate::Subscription
14#[derive(Debug)]
15pub struct Runtime<Executor, Sender, Message> {
16    executor: Executor,
17    sender: Sender,
18    subscriptions: subscription::Tracker,
19    _message: PhantomData<Message>,
20}
21
22impl<Executor, Sender, Message> Runtime<Executor, Sender, Message>
23where
24    Executor: self::Executor,
25    Sender: Sink<Message, Error = mpsc::SendError>
26        + Unpin
27        + MaybeSend
28        + Clone
29        + 'static,
30    Message: MaybeSend + 'static,
31{
32    /// Creates a new empty [`Runtime`].
33    ///
34    /// You need to provide:
35    /// - an [`Executor`] to spawn futures
36    /// - a `Sender` implementing `Sink` to receive the results
37    pub fn new(executor: Executor, sender: Sender) -> Self {
38        Self {
39            executor,
40            sender,
41            subscriptions: subscription::Tracker::new(),
42            _message: PhantomData,
43        }
44    }
45
46    /// Runs the given closure inside the [`Executor`] of the [`Runtime`].
47    ///
48    /// See [`Executor::enter`] to learn more.
49    pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
50        self.executor.enter(f)
51    }
52
53    /// Spawns a [`Future`] in the [`Runtime`].
54    ///
55    /// The resulting `Message` will be forwarded to the `Sender` of the
56    /// [`Runtime`].
57    ///
58    /// [`Future`]: BoxFuture
59    pub fn spawn(&mut self, future: BoxFuture<Message>) {
60        use futures::{FutureExt, SinkExt};
61
62        let mut sender = self.sender.clone();
63
64        let future = future.then(|message| async move {
65            let _ = sender.send(message).await;
66        });
67
68        self.executor.spawn(future);
69    }
70
71    /// Runs a [`Stream`] in the [`Runtime`] until completion.
72    ///
73    /// The resulting `Message`s will be forwarded to the `Sender` of the
74    /// [`Runtime`].
75    ///
76    /// [`Stream`]: BoxStream
77    pub fn run(&mut self, stream: BoxStream<Message>) {
78        use futures::{FutureExt, StreamExt};
79
80        let sender = self.sender.clone();
81        let future =
82            stream.map(Ok).forward(sender).map(|result| match result {
83                Ok(()) => (),
84                Err(error) => {
85                    log::warn!(
86                        "Stream could not run until completion: {error}"
87                    );
88                }
89            });
90
91        self.executor.spawn(future);
92    }
93
94    /// Tracks a [`Subscription`] in the [`Runtime`].
95    ///
96    /// It will spawn new streams or close old ones as necessary! See
97    /// [`Tracker::update`] to learn more about this!
98    ///
99    /// [`Tracker::update`]: subscription::Tracker::update
100    /// [`Subscription`]: crate::Subscription
101    pub fn track(
102        &mut self,
103        recipes: impl IntoIterator<
104            Item = Box<dyn subscription::Recipe<Output = Message>>,
105        >,
106    ) {
107        let Runtime {
108            executor,
109            subscriptions,
110            sender,
111            ..
112        } = self;
113
114        let futures = executor.enter(|| {
115            subscriptions.update(recipes.into_iter(), sender.clone())
116        });
117
118        for future in futures {
119            executor.spawn(future);
120        }
121    }
122
123    /// Broadcasts an event to all the subscriptions currently alive in the
124    /// [`Runtime`].
125    ///
126    /// See [`Tracker::broadcast`] to learn more.
127    ///
128    /// [`Tracker::broadcast`]: subscription::Tracker::broadcast
129    pub fn broadcast(&mut self, event: subscription::Event) {
130        self.subscriptions.broadcast(event);
131    }
132}