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()
}
}