Write docs for subscriptions and reorganize a bit
This commit is contained in:
parent
293314405f
commit
d6c3da21f7
@ -34,8 +34,8 @@ impl<T> Command<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`Command`] that performs the actions of all the givens
|
/// Creates a [`Command`] that performs the actions of all the given
|
||||||
/// futures.
|
/// commands.
|
||||||
///
|
///
|
||||||
/// Once this command is run, all the futures will be exectued at once.
|
/// Once this command is run, all the futures will be exectued at once.
|
||||||
///
|
///
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
//! [Iced]: https://github.com/hecrj/iced
|
//! [Iced]: https://github.com/hecrj/iced
|
||||||
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
|
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
|
||||||
//! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web
|
//! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web
|
||||||
//#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
#![deny(unused_results)]
|
#![deny(unused_results)]
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
//! Generate events asynchronously for you application.
|
//! Listen to external events in your application.
|
||||||
|
|
||||||
/// An event subscription.
|
/// A request to listen to external events.
|
||||||
|
///
|
||||||
|
/// Besides performing async actions on demand with [`Command`], most
|
||||||
|
/// applications also need to listen to external events passively.
|
||||||
|
///
|
||||||
|
/// A [`Subscription`] is normally provided to some runtime, like a [`Command`],
|
||||||
|
/// and it will generate events as long as the user keeps requesting it.
|
||||||
|
///
|
||||||
|
/// For instance, you can use a [`Subscription`] to listen to a WebSocket
|
||||||
|
/// connection, keyboard presses, mouse events, time ticks, etc.
|
||||||
|
///
|
||||||
|
/// This type is normally aliased by runtimes with a specific `Input` and/or
|
||||||
|
/// `Hasher`.
|
||||||
|
///
|
||||||
|
/// [`Command`]: ../struct.Command.html
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub struct Subscription<Hasher, Input, Output> {
|
pub struct Subscription<Hasher, Input, Output> {
|
||||||
recipes: Vec<Box<dyn Recipe<Hasher, Input, Output = Output>>>,
|
recipes: Vec<Box<dyn Recipe<Hasher, Input, Output = Output>>>,
|
||||||
}
|
}
|
||||||
@ -9,12 +24,19 @@ impl<H, I, O> Subscription<H, I, O>
|
|||||||
where
|
where
|
||||||
H: std::hash::Hasher,
|
H: std::hash::Hasher,
|
||||||
{
|
{
|
||||||
|
/// Returns an empty [`Subscription`] that will not produce any output.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
Self {
|
Self {
|
||||||
recipes: Vec::new(),
|
recipes: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
/// [`Recipe`]: trait.Recipe.html
|
||||||
pub fn from_recipe(
|
pub fn from_recipe(
|
||||||
recipe: impl Recipe<H, I, Output = O> + 'static,
|
recipe: impl Recipe<H, I, Output = O> + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -23,6 +45,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Batches all the provided subscriptions and returns the resulting
|
||||||
|
/// [`Subscription`].
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub fn batch(
|
pub fn batch(
|
||||||
subscriptions: impl Iterator<Item = Subscription<H, I, O>>,
|
subscriptions: impl Iterator<Item = Subscription<H, I, O>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -33,10 +59,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the different recipes of the [`Subscription`].
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub fn recipes(self) -> Vec<Box<dyn Recipe<H, I, Output = O>>> {
|
pub fn recipes(self) -> Vec<Box<dyn Recipe<H, I, Output = O>>> {
|
||||||
self.recipes
|
self.recipes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transforms the [`Subscription`] output with the given function.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub fn map<A>(
|
pub fn map<A>(
|
||||||
mut self,
|
mut self,
|
||||||
f: impl Fn(O) -> A + Send + Sync + 'static,
|
f: impl Fn(O) -> A + Send + Sync + 'static,
|
||||||
@ -68,12 +100,37 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The connection of an event subscription.
|
/// The description of a [`Subscription`].
|
||||||
|
///
|
||||||
|
/// A [`Recipe`] is the internal definition of a [`Subscription`]. It is used
|
||||||
|
/// by runtimes to run and identify subscriptions. You can use it to create your
|
||||||
|
/// own!
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
/// [`Recipe`]: trait.Recipe.html
|
||||||
pub trait Recipe<Hasher: std::hash::Hasher, Input> {
|
pub trait Recipe<Hasher: std::hash::Hasher, Input> {
|
||||||
|
/// The events that will be produced by a [`Subscription`] with this
|
||||||
|
/// [`Recipe`].
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
/// [`Recipe`]: trait.Recipe.html
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
/// Hashes the [`Recipe`].
|
||||||
|
///
|
||||||
|
/// This is used by runtimes to uniquely identify a [`Subscription`].
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
/// [`Recipe`]: trait.Recipe.html
|
||||||
fn hash(&self, state: &mut Hasher);
|
fn hash(&self, state: &mut Hasher);
|
||||||
|
|
||||||
|
/// Executes the [`Recipe`] and produces the stream of events of its
|
||||||
|
/// [`Subscription`].
|
||||||
|
///
|
||||||
|
/// It receives some generic `Input`, which is normally defined by runtimes.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
/// [`Recipe`]: trait.Recipe.html
|
||||||
fn stream(
|
fn stream(
|
||||||
self: Box<Self>,
|
self: Box<Self>,
|
||||||
input: Input,
|
input: Input,
|
||||||
|
@ -47,7 +47,7 @@ impl Application for Events {
|
|||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscriptions(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
if self.enabled {
|
if self.enabled {
|
||||||
iced_native::subscription::events().map(Message::EventOccurred)
|
iced_native::subscription::events().map(Message::EventOccurred)
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +74,7 @@ impl Application for Stopwatch {
|
|||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscriptions(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Idle => Subscription::none(),
|
State::Idle => Subscription::none(),
|
||||||
State::Ticking { .. } => {
|
State::Ticking { .. } => {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
//! [`Windowed`]: renderer/trait.Windowed.html
|
//! [`Windowed`]: renderer/trait.Windowed.html
|
||||||
//! [`UserInterface`]: struct.UserInterface.html
|
//! [`UserInterface`]: struct.UserInterface.html
|
||||||
//! [renderer]: renderer/index.html
|
//! [renderer]: renderer/index.html
|
||||||
//#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
#![deny(unused_results)]
|
#![deny(unused_results)]
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
@ -1,16 +1,42 @@
|
|||||||
|
//! Listen to external events in your application.
|
||||||
use crate::{Event, Hasher};
|
use crate::{Event, Hasher};
|
||||||
use futures::stream::BoxStream;
|
use futures::stream::BoxStream;
|
||||||
|
|
||||||
pub type EventStream = BoxStream<'static, Event>;
|
/// A request to listen to external events.
|
||||||
|
///
|
||||||
|
/// Besides performing async actions on demand with [`Command`], most
|
||||||
|
/// applications also need to listen to external events passively.
|
||||||
|
///
|
||||||
|
/// A [`Subscription`] is normally provided to some runtime, like a [`Command`],
|
||||||
|
/// and it will generate events as long as the user keeps requesting it.
|
||||||
|
///
|
||||||
|
/// For instance, you can use a [`Subscription`] to listen to a WebSocket
|
||||||
|
/// connection, keyboard presses, mouse events, time ticks, etc.
|
||||||
|
///
|
||||||
|
/// [`Command`]: ../struct.Command.html
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
pub type Subscription<T> = iced_core::Subscription<Hasher, EventStream, T>;
|
pub type Subscription<T> = iced_core::Subscription<Hasher, EventStream, T>;
|
||||||
|
|
||||||
|
/// A stream of runtime events.
|
||||||
|
///
|
||||||
|
/// It is the input of a [`Subscription`] in the native runtime.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: type.Subscription.html
|
||||||
|
pub type EventStream = BoxStream<'static, Event>;
|
||||||
|
|
||||||
pub use iced_core::subscription::Recipe;
|
pub use iced_core::subscription::Recipe;
|
||||||
|
|
||||||
mod events;
|
mod events;
|
||||||
|
|
||||||
use events::Events;
|
use events::Events;
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] to all the runtime events.
|
||||||
|
///
|
||||||
|
/// This subscription will notify your application of any [`Event`] handled by
|
||||||
|
/// the runtime.
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: type.Subscription.html
|
||||||
|
/// [`Event`]: ../enum.Event.html
|
||||||
pub fn events() -> Subscription<Event> {
|
pub fn events() -> Subscription<Event> {
|
||||||
Subscription::from_recipe(Events)
|
Subscription::from_recipe(Events)
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,17 @@ pub trait Application: Sized {
|
|||||||
/// [`Command`]: struct.Command.html
|
/// [`Command`]: struct.Command.html
|
||||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
|
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
|
||||||
|
|
||||||
/// TODO
|
/// Returns the event [`Subscription`] for the current state of the
|
||||||
fn subscriptions(&self) -> Subscription<Self::Message> {
|
/// application.
|
||||||
|
///
|
||||||
|
/// A [`Subscription`] will be kept alive as long as you keep returning it,
|
||||||
|
/// and the __messages__ produced will be handled by
|
||||||
|
/// [`update`](#tymethod.update).
|
||||||
|
///
|
||||||
|
/// By default, this method returns an empty [`Subscription`].
|
||||||
|
///
|
||||||
|
/// [`Subscription`]: struct.Subscription.html
|
||||||
|
fn subscription(&self) -> Subscription<Self::Message> {
|
||||||
Subscription::none()
|
Subscription::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +182,8 @@ where
|
|||||||
self.0.update(message)
|
self.0.update(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscriptions(&self) -> Subscription<Self::Message> {
|
fn subscription(&self) -> Subscription<Self::Message> {
|
||||||
self.0.subscriptions()
|
self.0.subscription()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&mut self) -> Element<'_, Self::Message> {
|
fn view(&mut self) -> Element<'_, Self::Message> {
|
||||||
|
@ -149,7 +149,7 @@ where
|
|||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscriptions(&self) -> Subscription<T::Message> {
|
fn subscription(&self) -> Subscription<T::Message> {
|
||||||
Subscription::none()
|
Subscription::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,9 @@ use crate::{
|
|||||||
conversion,
|
conversion,
|
||||||
input::{keyboard, mouse},
|
input::{keyboard, mouse},
|
||||||
renderer::{Target, Windowed},
|
renderer::{Target, Windowed},
|
||||||
Cache, Command, Container, Debug, Element, Event, Hasher, Length,
|
subscription, Cache, Command, Container, Debug, Element, Event, Length,
|
||||||
MouseCursor, Settings, Subscription, UserInterface,
|
MouseCursor, Settings, Subscription, UserInterface,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// An interactive, native cross-platform application.
|
/// An interactive, native cross-platform application.
|
||||||
///
|
///
|
||||||
@ -58,8 +57,14 @@ pub trait Application: Sized {
|
|||||||
/// [`Command`]: struct.Command.html
|
/// [`Command`]: struct.Command.html
|
||||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
|
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
|
||||||
|
|
||||||
/// TODO
|
/// Returns the event `Subscription` for the current state of the
|
||||||
fn subscriptions(&self) -> Subscription<Self::Message>;
|
/// application.
|
||||||
|
///
|
||||||
|
/// The messages produced by the `Subscription` will be handled by
|
||||||
|
/// [`update`](#tymethod.update).
|
||||||
|
///
|
||||||
|
/// A `Subscription` will be kept alive as long as you keep returning it!
|
||||||
|
fn subscription(&self) -> Subscription<Self::Message>;
|
||||||
|
|
||||||
/// Returns the widgets to display in the [`Application`].
|
/// Returns the widgets to display in the [`Application`].
|
||||||
///
|
///
|
||||||
@ -93,14 +98,14 @@ pub trait Application: Sized {
|
|||||||
let proxy = event_loop.create_proxy();
|
let proxy = event_loop.create_proxy();
|
||||||
let mut thread_pool =
|
let mut thread_pool =
|
||||||
futures::executor::ThreadPool::new().expect("Create thread pool");
|
futures::executor::ThreadPool::new().expect("Create thread pool");
|
||||||
let mut alive_subscriptions = Subscriptions::new();
|
let mut subscription_pool = subscription::Pool::new();
|
||||||
let mut external_messages = Vec::new();
|
let mut external_messages = Vec::new();
|
||||||
|
|
||||||
let (mut application, init_command) = Self::new();
|
let (mut application, init_command) = Self::new();
|
||||||
spawn(init_command, &mut thread_pool, &proxy);
|
spawn(init_command, &mut thread_pool, &proxy);
|
||||||
|
|
||||||
let subscriptions = application.subscriptions();
|
let subscription = application.subscription();
|
||||||
alive_subscriptions.update(subscriptions, &mut thread_pool, &proxy);
|
subscription_pool.update(subscription, &mut thread_pool, &proxy);
|
||||||
|
|
||||||
let mut title = application.title();
|
let mut title = application.title();
|
||||||
|
|
||||||
@ -184,9 +189,9 @@ pub trait Application: Sized {
|
|||||||
debug.layout_finished();
|
debug.layout_finished();
|
||||||
|
|
||||||
debug.event_processing_started();
|
debug.event_processing_started();
|
||||||
events
|
events.iter().for_each(|event| {
|
||||||
.iter()
|
subscription_pool.broadcast_event(*event)
|
||||||
.for_each(|event| alive_subscriptions.send_event(*event));
|
});
|
||||||
|
|
||||||
let mut messages =
|
let mut messages =
|
||||||
user_interface.update(&renderer, events.drain(..));
|
user_interface.update(&renderer, events.drain(..));
|
||||||
@ -215,9 +220,9 @@ pub trait Application: Sized {
|
|||||||
debug.update_finished();
|
debug.update_finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
let subscriptions = application.subscriptions();
|
let subscription = application.subscription();
|
||||||
alive_subscriptions.update(
|
subscription_pool.update(
|
||||||
subscriptions,
|
subscription,
|
||||||
&mut thread_pool,
|
&mut thread_pool,
|
||||||
&proxy,
|
&proxy,
|
||||||
);
|
);
|
||||||
@ -424,99 +429,6 @@ fn spawn<Message: Send>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Subscriptions {
|
|
||||||
alive: HashMap<u64, Connection>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Connection {
|
|
||||||
_cancel: futures::channel::oneshot::Sender<()>,
|
|
||||||
listener: Option<futures::channel::mpsc::Sender<Event>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Subscriptions {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
alive: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update<Message: Send>(
|
|
||||||
&mut self,
|
|
||||||
subscriptions: Subscription<Message>,
|
|
||||||
thread_pool: &mut futures::executor::ThreadPool,
|
|
||||||
proxy: &winit::event_loop::EventLoopProxy<Message>,
|
|
||||||
) {
|
|
||||||
use futures::{future::FutureExt, stream::StreamExt};
|
|
||||||
|
|
||||||
let recipes = subscriptions.recipes();
|
|
||||||
let mut alive = std::collections::HashSet::new();
|
|
||||||
|
|
||||||
for recipe in recipes {
|
|
||||||
let id = {
|
|
||||||
use std::hash::Hasher as _;
|
|
||||||
|
|
||||||
let mut hasher = Hasher::default();
|
|
||||||
recipe.hash(&mut hasher);
|
|
||||||
|
|
||||||
hasher.finish()
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = alive.insert(id);
|
|
||||||
|
|
||||||
if !self.alive.contains_key(&id) {
|
|
||||||
let (cancel, cancelled) = futures::channel::oneshot::channel();
|
|
||||||
let (event_sender, event_receiver) =
|
|
||||||
futures::channel::mpsc::channel(100);
|
|
||||||
|
|
||||||
let stream = recipe.stream(event_receiver.boxed());
|
|
||||||
let proxy = proxy.clone();
|
|
||||||
|
|
||||||
let future = futures::future::select(
|
|
||||||
cancelled,
|
|
||||||
stream.for_each(move |message| {
|
|
||||||
proxy
|
|
||||||
.send_event(message)
|
|
||||||
.expect("Send subscription result to event loop");
|
|
||||||
|
|
||||||
futures::future::ready(())
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.map(|_| ());
|
|
||||||
|
|
||||||
thread_pool.spawn_ok(future);
|
|
||||||
|
|
||||||
let _ = self.alive.insert(
|
|
||||||
id,
|
|
||||||
Connection {
|
|
||||||
_cancel: cancel,
|
|
||||||
listener: if event_sender.is_closed() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(event_sender)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.alive.retain(|id, _| alive.contains(&id));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_event(&mut self, event: Event) {
|
|
||||||
self.alive
|
|
||||||
.values_mut()
|
|
||||||
.filter_map(|connection| connection.listener.as_mut())
|
|
||||||
.for_each(|listener| {
|
|
||||||
if let Err(error) = listener.try_send(event) {
|
|
||||||
log::warn!(
|
|
||||||
"Error sending event to subscription: {:?}",
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// As defined in: http://www.unicode.org/faq/private_use.html
|
// As defined in: http://www.unicode.org/faq/private_use.html
|
||||||
// TODO: Remove once https://github.com/rust-windowing/winit/pull/1254 lands
|
// TODO: Remove once https://github.com/rust-windowing/winit/pull/1254 lands
|
||||||
fn is_private_use_character(c: char) -> bool {
|
fn is_private_use_character(c: char) -> bool {
|
||||||
|
@ -29,6 +29,7 @@ pub mod conversion;
|
|||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
mod application;
|
mod application;
|
||||||
|
mod subscription;
|
||||||
|
|
||||||
pub use application::Application;
|
pub use application::Application;
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
|
97
winit/src/subscription.rs
Normal file
97
winit/src/subscription.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use iced_native::{Event, Hasher, Subscription};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct Pool {
|
||||||
|
alive: HashMap<u64, Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Handle {
|
||||||
|
_cancel: futures::channel::oneshot::Sender<()>,
|
||||||
|
listener: Option<futures::channel::mpsc::Sender<Event>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pool {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
alive: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<Message: Send>(
|
||||||
|
&mut self,
|
||||||
|
subscription: Subscription<Message>,
|
||||||
|
thread_pool: &mut futures::executor::ThreadPool,
|
||||||
|
proxy: &winit::event_loop::EventLoopProxy<Message>,
|
||||||
|
) {
|
||||||
|
use futures::{future::FutureExt, stream::StreamExt};
|
||||||
|
|
||||||
|
let recipes = subscription.recipes();
|
||||||
|
let mut alive = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
for recipe in recipes {
|
||||||
|
let id = {
|
||||||
|
use std::hash::Hasher as _;
|
||||||
|
|
||||||
|
let mut hasher = Hasher::default();
|
||||||
|
recipe.hash(&mut hasher);
|
||||||
|
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = alive.insert(id);
|
||||||
|
|
||||||
|
if !self.alive.contains_key(&id) {
|
||||||
|
let (cancel, cancelled) = futures::channel::oneshot::channel();
|
||||||
|
|
||||||
|
// TODO: Use bus if/when it supports async
|
||||||
|
let (event_sender, event_receiver) =
|
||||||
|
futures::channel::mpsc::channel(100);
|
||||||
|
|
||||||
|
let stream = recipe.stream(event_receiver.boxed());
|
||||||
|
let proxy = proxy.clone();
|
||||||
|
|
||||||
|
let future = futures::future::select(
|
||||||
|
cancelled,
|
||||||
|
stream.for_each(move |message| {
|
||||||
|
proxy
|
||||||
|
.send_event(message)
|
||||||
|
.expect("Send subscription result to event loop");
|
||||||
|
|
||||||
|
futures::future::ready(())
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map(|_| ());
|
||||||
|
|
||||||
|
thread_pool.spawn_ok(future);
|
||||||
|
|
||||||
|
let _ = self.alive.insert(
|
||||||
|
id,
|
||||||
|
Handle {
|
||||||
|
_cancel: cancel,
|
||||||
|
listener: if event_sender.is_closed() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(event_sender)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.alive.retain(|id, _| alive.contains(&id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcast_event(&mut self, event: Event) {
|
||||||
|
self.alive
|
||||||
|
.values_mut()
|
||||||
|
.filter_map(|connection| connection.listener.as_mut())
|
||||||
|
.for_each(|listener| {
|
||||||
|
if let Err(error) = listener.try_send(event) {
|
||||||
|
log::warn!(
|
||||||
|
"Error sending event to subscription: {:?}",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user