summaryrefslogtreecommitdiffstats
path: root/futures
diff options
context:
space:
mode:
Diffstat (limited to 'futures')
-rw-r--r--futures/Cargo.toml15
-rw-r--r--futures/src/command.rs17
-rw-r--r--futures/src/executor.rs20
-rw-r--r--futures/src/executor/smol.rs18
-rw-r--r--futures/src/executor/tokio.rs3
-rw-r--r--futures/src/executor/tokio_old.rs21
-rw-r--r--futures/src/lib.rs18
-rw-r--r--futures/src/runtime.rs22
-rw-r--r--futures/src/subscription.rs53
-rw-r--r--futures/src/subscription/tracker.rs10
-rw-r--r--futures/src/time.rs55
11 files changed, 145 insertions, 107 deletions
diff --git a/futures/Cargo.toml b/futures/Cargo.toml
index 275d0391..3cea6e1a 100644
--- a/futures/Cargo.toml
+++ b/futures/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_futures"
-version = "0.1.2"
+version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "Commands, subscriptions, and runtimes for Iced"
@@ -19,16 +19,27 @@ log = "0.4"
[dependencies.futures]
version = "0.3"
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio_old]
+package = "tokio"
version = "0.2"
optional = true
features = ["rt-core", "rt-threaded", "time", "stream"]
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
+package = "tokio"
+version = "1.0"
+optional = true
+features = ["rt", "rt-multi-thread", "time"]
+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.async-std]
version = "1.0"
optional = true
features = ["unstable"]
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.smol]
+version = "1.2"
+optional = true
+
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4"
diff --git a/futures/src/command.rs b/futures/src/command.rs
index 063e9b68..b06ab3f8 100644
--- a/futures/src/command.rs
+++ b/futures/src/command.rs
@@ -5,9 +5,6 @@ use futures::future::{Future, FutureExt};
///
/// You should be able to turn a future easily into a [`Command`], either by
/// using the `From` trait or [`Command::perform`].
-///
-/// [`Command`]: struct.Command.html
-/// [`Command::perform`]: #method.perform
pub struct Command<T> {
futures: Vec<BoxFuture<T>>,
}
@@ -16,8 +13,6 @@ impl<T> Command<T> {
/// Creates an empty [`Command`].
///
/// In other words, a [`Command`] that does nothing.
- ///
- /// [`Command`]: struct.Command.html
pub fn none() -> Self {
Self {
futures: Vec::new(),
@@ -25,8 +20,6 @@ impl<T> Command<T> {
}
/// Creates a [`Command`] that performs the action of the given future.
- ///
- /// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn perform<A>(
future: impl Future<Output = T> + 'static + Send,
@@ -38,8 +31,6 @@ impl<T> Command<T> {
}
/// Creates a [`Command`] that performs the action of the given future.
- ///
- /// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn perform<A>(
future: impl Future<Output = T> + 'static,
@@ -51,8 +42,6 @@ impl<T> Command<T> {
}
/// Applies a transformation to the result of a [`Command`].
- ///
- /// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn map<A>(
mut self,
@@ -78,8 +67,6 @@ impl<T> Command<T> {
}
/// Applies a transformation to the result of a [`Command`].
- ///
- /// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static) -> Command<A>
where
@@ -105,8 +92,6 @@ impl<T> Command<T> {
/// commands.
///
/// Once this command is run, all the commands will be executed at once.
- ///
- /// [`Command`]: struct.Command.html
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
Self {
futures: commands
@@ -117,8 +102,6 @@ impl<T> Command<T> {
}
/// Converts a [`Command`] into its underlying list of futures.
- ///
- /// [`Command`]: struct.Command.html
pub fn futures(self) -> Vec<BoxFuture<T>> {
self.futures
}
diff --git a/futures/src/executor.rs b/futures/src/executor.rs
index cbd34ee8..b35b5bc1 100644
--- a/futures/src/executor.rs
+++ b/futures/src/executor.rs
@@ -7,9 +7,15 @@ mod thread_pool;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
mod tokio;
+#[cfg(all(not(target_arch = "wasm32"), feature = "tokio_old"))]
+mod tokio_old;
+
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
mod async_std;
+#[cfg(all(not(target_arch = "wasm32"), feature = "smol"))]
+mod smol;
+
#[cfg(target_arch = "wasm32")]
mod wasm_bindgen;
@@ -21,9 +27,15 @@ pub use thread_pool::ThreadPool;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
pub use self::tokio::Tokio;
+#[cfg(all(not(target_arch = "wasm32"), feature = "tokio_old"))]
+pub use self::tokio_old::TokioOld;
+
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
pub use self::async_std::AsyncStd;
+#[cfg(all(not(target_arch = "wasm32"), feature = "smol"))]
+pub use self::smol::Smol;
+
#[cfg(target_arch = "wasm32")]
pub use wasm_bindgen::WasmBindgen;
@@ -32,21 +44,15 @@ use futures::Future;
/// A type that can run futures.
pub trait Executor: Sized {
/// Creates a new [`Executor`].
- ///
- /// [`Executor`]: trait.Executor.html
fn new() -> Result<Self, futures::io::Error>
where
Self: Sized;
/// Spawns a future in the [`Executor`].
- ///
- /// [`Executor`]: trait.Executor.html
#[cfg(not(target_arch = "wasm32"))]
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static);
/// Spawns a local future in the [`Executor`].
- ///
- /// [`Executor`]: trait.Executor.html
#[cfg(target_arch = "wasm32")]
fn spawn(&self, future: impl Future<Output = ()> + 'static);
@@ -56,8 +62,6 @@ pub trait Executor: Sized {
/// before creating futures. This method can be leveraged to set up this
/// global state, call a function, restore the state, and obtain the result
/// of the call.
- ///
- /// [`Executor`]: trait.Executor.html
fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
f()
}
diff --git a/futures/src/executor/smol.rs b/futures/src/executor/smol.rs
new file mode 100644
index 00000000..deafd43a
--- /dev/null
+++ b/futures/src/executor/smol.rs
@@ -0,0 +1,18 @@
+use crate::Executor;
+
+use futures::Future;
+
+/// A `smol` runtime.
+#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
+#[derive(Debug)]
+pub struct Smol;
+
+impl Executor for Smol {
+ fn new() -> Result<Self, futures::io::Error> {
+ Ok(Self)
+ }
+
+ fn spawn(&self, future: impl Future<Output = ()> + Send + 'static) {
+ smol::spawn(future).detach();
+ }
+}
diff --git a/futures/src/executor/tokio.rs b/futures/src/executor/tokio.rs
index a730bce8..c6a21cec 100644
--- a/futures/src/executor/tokio.rs
+++ b/futures/src/executor/tokio.rs
@@ -16,6 +16,7 @@ impl Executor for Tokio {
}
fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
- tokio::runtime::Runtime::enter(self, f)
+ let _guard = tokio::runtime::Runtime::enter(self);
+ f()
}
}
diff --git a/futures/src/executor/tokio_old.rs b/futures/src/executor/tokio_old.rs
new file mode 100644
index 00000000..d64729fa
--- /dev/null
+++ b/futures/src/executor/tokio_old.rs
@@ -0,0 +1,21 @@
+use crate::Executor;
+
+use futures::Future;
+
+/// An old `tokio` runtime.
+#[cfg_attr(docsrs, doc(cfg(feature = "tokio_old")))]
+pub type TokioOld = tokio_old::runtime::Runtime;
+
+impl Executor for TokioOld {
+ fn new() -> Result<Self, futures::io::Error> {
+ tokio_old::runtime::Runtime::new()
+ }
+
+ fn spawn(&self, future: impl Future<Output = ()> + Send + 'static) {
+ let _ = tokio_old::runtime::Runtime::spawn(self, future);
+ }
+
+ fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
+ tokio_old::runtime::Runtime::enter(self, f)
+ }
+}
diff --git a/futures/src/lib.rs b/futures/src/lib.rs
index 46fc59fc..01cf5c89 100644
--- a/futures/src/lib.rs
+++ b/futures/src/lib.rs
@@ -1,4 +1,6 @@
//! Asynchronous tasks for GUI programming, inspired by Elm.
+//!
+//! ![The foundations of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
@@ -15,10 +17,22 @@ pub mod executor;
pub mod subscription;
#[cfg(all(
- any(feature = "tokio", feature = "async-std"),
+ any(
+ feature = "tokio",
+ feature = "tokio_old",
+ feature = "async-std",
+ feature = "smol"
+ ),
not(target_arch = "wasm32")
))]
-#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async-std"))))]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(
+ feature = "tokio",
+ feature = "async-std",
+ feature = "smol"
+ )))
+)]
pub mod time;
pub use command::Command;
diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs
index d204670b..e56a4eb0 100644
--- a/futures/src/runtime.rs
+++ b/futures/src/runtime.rs
@@ -8,11 +8,6 @@ use std::marker::PhantomData;
///
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
/// [`Command`] or [`Subscription`] and get notified of the results!
-///
-/// [`Runtime`]: struct.Runtime.html
-/// [`Executor`]: executor/trait.Executor.html
-/// [`Command`]: struct.Command.html
-/// [`Subscription`]: subscription/struct.Subscription.html
#[derive(Debug)]
pub struct Runtime<Hasher, Event, Executor, Sender, Message> {
executor: Executor,
@@ -36,8 +31,6 @@ where
/// You need to provide:
/// - an [`Executor`] to spawn futures
/// - a `Sender` implementing `Sink` to receive the results
- ///
- /// [`Runtime`]: struct.Runtime.html
pub fn new(executor: Executor, sender: Sender) -> Self {
Self {
executor,
@@ -50,10 +43,6 @@ where
/// Runs the given closure inside the [`Executor`] of the [`Runtime`].
///
/// See [`Executor::enter`] to learn more.
- ///
- /// [`Executor`]: executor/trait.Executor.html
- /// [`Runtime`]: struct.Runtime.html
- /// [`Executor::enter`]: executor/trait.Executor.html#method.enter
pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
self.executor.enter(f)
}
@@ -62,9 +51,6 @@ where
///
/// The resulting `Message` will be forwarded to the `Sender` of the
/// [`Runtime`].
- ///
- /// [`Command`]: struct.Command.html
- /// [`Runtime`]: struct.Runtime.html
pub fn spawn(&mut self, command: Command<Message>) {
use futures::{FutureExt, SinkExt};
@@ -88,9 +74,7 @@ where
/// It will spawn new streams or close old ones as necessary! See
/// [`Tracker::update`] to learn more about this!
///
- /// [`Subscription`]: subscription/struct.Subscription.html
- /// [`Runtime`]: struct.Runtime.html
- /// [`Tracker::update`]: subscription/struct.Tracker.html#method.update
+ /// [`Tracker::update`]: subscription::Tracker::update
pub fn track(
&mut self,
subscription: Subscription<Hasher, Event, Message>,
@@ -115,9 +99,7 @@ where
///
/// See [`Tracker::broadcast`] to learn more.
///
- /// [`Runtime`]: struct.Runtime.html
- /// [`Tracker::broadcast`]:
- /// subscription/struct.Tracker.html#method.broadcast
+ /// [`Tracker::broadcast`]: subscription::Tracker::broadcast
pub fn broadcast(&mut self, event: Event) {
self.subscriptions.broadcast(event);
}
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index 7a75fc31..e60ad79a 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -19,8 +19,7 @@ use crate::BoxStream;
/// This type is normally aliased by runtimes with a specific `Event` and/or
/// `Hasher`.
///
-/// [`Command`]: ../struct.Command.html
-/// [`Subscription`]: struct.Subscription.html
+/// [`Command`]: crate::Command
pub struct Subscription<Hasher, Event, Output> {
recipes: Vec<Box<dyn Recipe<Hasher, Event, Output = Output>>>,
}
@@ -30,8 +29,6 @@ where
H: std::hash::Hasher,
{
/// Returns an empty [`Subscription`] that will not produce any output.
- ///
- /// [`Subscription`]: struct.Subscription.html
pub fn none() -> Self {
Self {
recipes: Vec::new(),
@@ -39,9 +36,6 @@ where
}
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
- ///
- /// [`Subscription`]: struct.Subscription.html
- /// [`Recipe`]: trait.Recipe.html
pub fn from_recipe(
recipe: impl Recipe<H, E, Output = O> + 'static,
) -> Self {
@@ -52,8 +46,6 @@ where
/// Batches all the provided subscriptions and returns the resulting
/// [`Subscription`].
- ///
- /// [`Subscription`]: struct.Subscription.html
pub fn batch(
subscriptions: impl IntoIterator<Item = Subscription<H, E, O>>,
) -> Self {
@@ -66,8 +58,6 @@ where
}
/// Returns the different recipes of the [`Subscription`].
- ///
- /// [`Subscription`]: struct.Subscription.html
pub fn recipes(self) -> Vec<Box<dyn Recipe<H, E, Output = O>>> {
self.recipes
}
@@ -75,12 +65,6 @@ where
/// Adds a value to the [`Subscription`] context.
///
/// The value will be part of the identity of a [`Subscription`].
- ///
- /// This is necessary if you want to use multiple instances of the same
- /// [`Subscription`] to produce different kinds of messages based on some
- /// external data.
- ///
- /// [`Subscription`]: struct.Subscription.html
pub fn with<T>(mut self, value: T) -> Subscription<H, E, (T, O)>
where
H: 'static,
@@ -101,26 +85,19 @@ where
}
/// Transforms the [`Subscription`] output with the given function.
- ///
- /// [`Subscription`]: struct.Subscription.html
- pub fn map<A>(
- mut self,
- f: impl Fn(O) -> A + Send + Sync + 'static,
- ) -> Subscription<H, E, A>
+ pub fn map<A>(mut self, f: fn(O) -> A) -> Subscription<H, E, A>
where
H: 'static,
E: 'static,
O: 'static,
A: 'static,
{
- let function = std::sync::Arc::new(f);
-
Subscription {
recipes: self
.recipes
.drain(..)
.map(|recipe| {
- Box::new(Map::new(recipe, function.clone()))
+ Box::new(Map::new(recipe, f))
as Box<dyn Recipe<H, E, Output = A>>
})
.collect(),
@@ -140,9 +117,6 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// by runtimes to run and identify subscriptions. You can use it to create your
/// own!
///
-/// [`Subscription`]: struct.Subscription.html
-/// [`Recipe`]: trait.Recipe.html
-///
/// # Examples
/// The repository has a couple of [examples] that use a custom [`Recipe`]:
///
@@ -151,23 +125,17 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
/// to listen to time.
///
-/// [examples]: https://github.com/hecrj/iced/tree/0.1/examples
-/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.1/examples/download_progress
-/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.1/examples/stopwatch
+/// [examples]: https://github.com/hecrj/iced/tree/0.3/examples
+/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.3/examples/download_progress
+/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.3/examples/stopwatch
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`].
- ///
- /// [`Subscription`]: struct.Subscription.html
- /// [`Recipe`]: trait.Recipe.html
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);
/// Executes the [`Recipe`] and produces the stream of events of its
@@ -175,9 +143,6 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
///
/// It receives some stream of generic events, which is normally defined by
/// shells.
- ///
- /// [`Subscription`]: struct.Subscription.html
- /// [`Recipe`]: trait.Recipe.html
fn stream(
self: Box<Self>,
input: BoxStream<Event>,
@@ -186,13 +151,13 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
struct Map<Hasher, Event, A, B> {
recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
- mapper: std::sync::Arc<dyn Fn(A) -> B + Send + Sync>,
+ mapper: fn(A) -> B,
}
impl<H, E, A, B> Map<H, E, A, B> {
fn new(
recipe: Box<dyn Recipe<H, E, Output = A>>,
- mapper: std::sync::Arc<dyn Fn(A) -> B + Send + Sync + 'static>,
+ mapper: fn(A) -> B,
) -> Self {
Map { recipe, mapper }
}
@@ -209,8 +174,8 @@ where
fn hash(&self, state: &mut H) {
use std::hash::Hash;
- std::any::TypeId::of::<B>().hash(state);
self.recipe.hash(state);
+ self.mapper.hash(state);
}
fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {
diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs
index c2a0d0f1..3a8d4a87 100644
--- a/futures/src/subscription/tracker.rs
+++ b/futures/src/subscription/tracker.rs
@@ -26,8 +26,6 @@ where
Event: 'static + Send + Clone,
{
/// Creates a new empty [`Tracker`].
- ///
- /// [`Tracker`]: struct.Tracker.html
pub fn new() -> Self {
Self {
subscriptions: HashMap::new(),
@@ -52,9 +50,7 @@ where
/// It returns a list of futures that need to be spawned to materialize
/// the [`Tracker`] changes.
///
- /// [`Tracker`]: struct.Tracker.html
- /// [`Subscription`]: struct.Subscription.html
- /// [`Recipe`]: trait.Recipe.html
+ /// [`Recipe`]: crate::subscription::Recipe
pub fn update<Message, Receiver>(
&mut self,
subscription: Subscription<Hasher, Event, Message>,
@@ -132,14 +128,14 @@ where
/// This method publishes the given event to all the subscription streams
/// currently open.
///
- /// [`Recipe::stream`]: trait.Recipe.html#tymethod.stream
+ /// [`Recipe::stream`]: crate::subscription::Recipe::stream
pub fn broadcast(&mut self, event: Event) {
self.subscriptions
.values_mut()
.filter_map(|connection| connection.listener.as_mut())
.for_each(|listener| {
if let Err(error) = listener.try_send(event.clone()) {
- log::error!(
+ log::warn!(
"Error sending event to subscription: {:?}",
error
);
diff --git a/futures/src/time.rs b/futures/src/time.rs
index e87b4a83..86b4a4e7 100644
--- a/futures/src/time.rs
+++ b/futures/src/time.rs
@@ -5,8 +5,6 @@ use crate::subscription::{self, Subscription};
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
-///
-/// [`Subscription`]: ../subscription/struct.Subscription.html
pub fn every<H: std::hash::Hasher, E>(
duration: std::time::Duration,
) -> Subscription<H, E, std::time::Instant> {
@@ -15,6 +13,33 @@ pub fn every<H: std::hash::Hasher, E>(
struct Every(std::time::Duration);
+#[cfg(all(
+ not(any(feature = "tokio_old", feature = "tokio", feature = "async-std")),
+ feature = "smol"
+))]
+impl<H, E> subscription::Recipe<H, E> for Every
+where
+ H: std::hash::Hasher,
+{
+ type Output = std::time::Instant;
+
+ fn hash(&self, state: &mut H) {
+ use std::hash::Hash;
+
+ std::any::TypeId::of::<Self>().hash(state);
+ self.0.hash(state);
+ }
+
+ fn stream(
+ self: Box<Self>,
+ _input: futures::stream::BoxStream<'static, E>,
+ ) -> futures::stream::BoxStream<'static, Self::Output> {
+ use futures::stream::StreamExt;
+
+ smol::Timer::interval(self.0).boxed()
+ }
+}
+
#[cfg(feature = "async-std")]
impl<H, E> subscription::Recipe<H, E> for Every
where
@@ -41,7 +66,10 @@ where
}
}
-#[cfg(all(feature = "tokio", not(feature = "async-std")))]
+#[cfg(all(
+ any(feature = "tokio", feature = "tokio_old"),
+ not(any(feature = "async-std", feature = "smol"))
+))]
impl<H, E> subscription::Recipe<H, E> for Every
where
H: std::hash::Hasher,
@@ -61,10 +89,25 @@ where
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
+ #[cfg(feature = "tokio_old")]
+ use tokio_old as tokio;
+
let start = tokio::time::Instant::now() + self.0;
- tokio::time::interval_at(start, self.0)
- .map(|_| std::time::Instant::now())
- .boxed()
+ let stream = {
+ #[cfg(feature = "tokio")]
+ {
+ futures::stream::unfold(
+ tokio::time::interval_at(start, self.0),
+ |mut interval| async move {
+ Some((interval.tick().await, interval))
+ },
+ )
+ }
+ #[cfg(feature = "tokio_old")]
+ tokio::time::interval_at(start, self.0)
+ };
+
+ stream.map(tokio::time::Instant::into_std).boxed()
}
}