Make Command implementations platform-specific

This allows us to introduce a platform-specific `Action` to both `iced_native`
and `iced_web` and remove the `Clipboard` from `Application::update` to maintain
purity.

Additionally, this should let us implement further actions to let users query
and modify the shell environment (e.g. window, clipboard, and more!)
This commit is contained in:
Héctor Ramón Jiménez 2021-09-01 19:21:49 +07:00
parent b7b7741578
commit 76698ff2b5
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
34 changed files with 363 additions and 342 deletions

View file

@ -1,138 +0,0 @@
use crate::BoxFuture;
use futures::future::{Future, FutureExt};
/// A collection of async operations.
///
/// You should be able to turn a future easily into a [`Command`], either by
/// using the `From` trait or [`Command::perform`].
pub struct Command<T> {
futures: Vec<BoxFuture<T>>,
}
impl<T> Command<T> {
/// Creates an empty [`Command`].
///
/// In other words, a [`Command`] that does nothing.
pub fn none() -> Self {
Self {
futures: Vec::new(),
}
}
/// Creates a [`Command`] that performs the action of the given future.
#[cfg(not(target_arch = "wasm32"))]
pub fn perform<A>(
future: impl Future<Output = T> + 'static + Send,
f: impl Fn(T) -> A + 'static + Send,
) -> Command<A> {
Command {
futures: vec![Box::pin(future.map(f))],
}
}
/// Creates a [`Command`] that performs the action of the given future.
#[cfg(target_arch = "wasm32")]
pub fn perform<A>(
future: impl Future<Output = T> + 'static,
f: impl Fn(T) -> A + 'static + Send,
) -> Command<A> {
Command {
futures: vec![Box::pin(future.map(f))],
}
}
/// Applies a transformation to the result of a [`Command`].
#[cfg(not(target_arch = "wasm32"))]
pub fn map<A>(
mut self,
f: impl Fn(T) -> A + 'static + Send + Sync,
) -> Command<A>
where
T: 'static,
{
let f = std::sync::Arc::new(f);
Command {
futures: self
.futures
.drain(..)
.map(|future| {
let f = f.clone();
Box::pin(future.map(move |result| f(result)))
as BoxFuture<A>
})
.collect(),
}
}
/// Applies a transformation to the result of a [`Command`].
#[cfg(target_arch = "wasm32")]
pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static) -> Command<A>
where
T: 'static,
{
let f = std::rc::Rc::new(f);
Command {
futures: self
.futures
.drain(..)
.map(|future| {
let f = f.clone();
Box::pin(future.map(move |result| f(result)))
as BoxFuture<A>
})
.collect(),
}
}
/// Creates a [`Command`] that performs the actions of all the given
/// commands.
///
/// Once this command is run, all the commands will be executed at once.
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
Self {
futures: commands
.into_iter()
.flat_map(|command| command.futures)
.collect(),
}
}
/// Converts a [`Command`] into its underlying list of futures.
pub fn futures(self) -> Vec<BoxFuture<T>> {
self.futures
}
}
#[cfg(not(target_arch = "wasm32"))]
impl<T, A> From<A> for Command<T>
where
A: Future<Output = T> + 'static + Send,
{
fn from(future: A) -> Self {
Self {
futures: vec![future.boxed()],
}
}
}
#[cfg(target_arch = "wasm32")]
impl<T, A> From<A> for Command<T>
where
A: Future<Output = T> + 'static,
{
fn from(future: A) -> Self {
Self {
futures: vec![future.boxed_local()],
}
}
}
impl<T> std::fmt::Debug for Command<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Command").finish()
}
}

View file

@ -10,7 +10,6 @@
pub use futures;
mod command;
mod runtime;
pub mod executor;
@ -35,7 +34,6 @@ pub mod subscription;
)]
pub mod time;
pub use command::Command;
pub use executor::Executor;
pub use runtime::Runtime;
pub use subscription::Subscription;

View file

@ -1,5 +1,6 @@
//! Run commands and keep track of subscriptions.
use crate::{subscription, Command, Executor, Subscription};
use crate::BoxFuture;
use crate::{subscription, Executor, Subscription};
use futures::{channel::mpsc, Sink};
use std::marker::PhantomData;
@ -51,22 +52,18 @@ where
///
/// The resulting `Message` will be forwarded to the `Sender` of the
/// [`Runtime`].
pub fn spawn(&mut self, command: Command<Message>) {
pub fn spawn(&mut self, future: BoxFuture<Message>) {
use futures::{FutureExt, SinkExt};
let futures = command.futures();
let mut sender = self.sender.clone();
for future in futures {
let mut sender = self.sender.clone();
let future = future.then(|message| async move {
let _ = sender.send(message).await;
let future = future.then(|message| async move {
let _ = sender.send(message).await;
()
});
()
});
self.executor.spawn(future);
}
self.executor.spawn(future);
}
/// Tracks a [`Subscription`] in the [`Runtime`].