Skip to content

Commit

Permalink
feat: Conditional types for futures and traits
Browse files Browse the repository at this point in the history
Signed-off-by: Lachezar Lechev <[email protected]>
  • Loading branch information
elpiel committed Jan 4, 2023
1 parent 9a75f2f commit 82b194d
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 87 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ members = [
doctest = false

[features]
# TODO: env-future-send should be enabled by default
# but our `TestEnv` for `unit_tests` uses a MutexGuard which is not Send.
# default = ["env-future-send"]

# Adds `Send` marker trait to the `Env` trait methods and `EnvFuture`.
# It's required for environments that do not support `Send`.
# If enabled for `wasm` it will do nothing since `wasm` does not support Send!
# see https://github.com/rustwasm/wasm-bindgen/issues/2833
env-future-send = []

[dependencies]
Expand Down
9 changes: 2 additions & 7 deletions src/addon_transport/http_transport/legacy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::addon_transport::AddonTransport;
use crate::runtime::{Env, EnvError, EnvFutureExt, TryEnvFuture};
use crate::runtime::{ConditionalSend, Env, EnvError, EnvFutureExt, TryEnvFuture};
use crate::types::addon::{Manifest, ResourcePath, ResourceResponse};
use crate::types::resource::{MetaItem, MetaItemPreview, Stream, Subtitles};
use futures::{future, TryFutureExt};
Expand Down Expand Up @@ -85,12 +85,7 @@ impl From<SubtitlesResult> for ResourceResponse {
}
}

fn map_response<
#[cfg(not(feature = "env-future-send"))] T: Sized + 'static,
#[cfg(feature = "env-future-send")] T: Sized + Send + 'static,
>(
resp: JsonRPCResp<T>,
) -> TryEnvFuture<T> {
fn map_response<T: Sized + ConditionalSend + 'static>(resp: JsonRPCResp<T>) -> TryEnvFuture<T> {
match resp {
JsonRPCResp::Result { result } => future::ok(result).boxed_env(),
JsonRPCResp::Error { error } => future::err(LegacyErr::JsonRPC(error).into()).boxed_env(),
Expand Down
9 changes: 2 additions & 7 deletions src/addon_transport/unsupported_transport.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::addon_transport::AddonTransport;
use crate::runtime::{EnvError, EnvFutureExt, TryEnvFuture};
use crate::runtime::{ConditionalSend, EnvError, EnvFutureExt, TryEnvFuture};
use crate::types::addon::{Manifest, ResourcePath, ResourceResponse};
use futures::future;
use url::Url;
Expand All @@ -12,12 +12,7 @@ impl UnsupportedTransport {
pub fn new(transport_url: Url) -> Self {
UnsupportedTransport { transport_url }
}
fn result<
#[cfg(not(feature = "env-future-send"))] T: Sized + 'static,
#[cfg(feature = "env-future-send")] T: Sized + Send + 'static,
>(
&self,
) -> TryEnvFuture<T> {
fn result<T: Sized + ConditionalSend + 'static>(&self) -> TryEnvFuture<T> {
future::err(EnvError::AddonTransport(format!(
"Unsupported addon transport: {}",
self.transport_url.scheme()
Expand Down
6 changes: 2 additions & 4 deletions src/runtime/effects.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::runtime::msg::Msg;
use derive_more::{From, IntoIterator};

#[cfg(not(feature = "env-future-send"))]
type Future = futures::future::LocalBoxFuture<'static, Msg>;
use super::EnvFuture;

#[cfg(feature = "env-future-send")]
type Future = futures::future::BoxFuture<'static, Msg>;
type Future = EnvFuture<'static, Msg>;

pub enum EffectFuture {
Concurrent(Future),
Expand Down
88 changes: 47 additions & 41 deletions src/runtime/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ use crate::constants::{
use crate::models::ctx::Ctx;
use crate::models::streaming_server::StreamingServer;
use chrono::{DateTime, Utc};
use futures::{future, Future, FutureExt, TryFutureExt};
use futures::{future, Future, TryFutureExt};
use http::Request;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize, Serializer};
use std::fmt;
use url::Url;

pub use conditional_types::{ConditionalSend, EnvFuture, EnvFutureExt};

#[derive(Clone, PartialEq, Debug)]
pub enum EnvError {
Fetch(String),
Expand Down Expand Up @@ -84,64 +86,68 @@ impl From<serde_json::Error> for EnvError {
}
}

#[cfg(not(feature = "env-future-send"))]
pub type EnvFuture<T> = futures::future::LocalBoxFuture<'static, T>;
#[cfg(any(not(feature = "env-future-send"), target_family = "wasm"))]
/// Only for wasm or when `env-future-send` is not enabled
mod conditional_types {
use futures::{future::LocalBoxFuture, Future, FutureExt};

#[cfg(feature = "env-future-send")]
pub type EnvFuture<T> = future::BoxFuture<'static, T>;
pub type EnvFuture<'a, T> = LocalBoxFuture<'a, T>;

impl<T: ?Sized> EnvFutureExt for T where T: Future {}
pub trait ConditionalSend {}

pub trait EnvFutureExt: Future {
#[cfg(not(feature = "env-future-send"))]
fn boxed_env<'a>(self) -> futures::future::LocalBoxFuture<'a, Self::Output>
where
Self: Sized + 'a,
{
self.boxed_local()
impl<T> ConditionalSend for T {}

pub trait EnvFutureExt: Future {
fn boxed_env<'a>(self) -> EnvFuture<'a, Self::Output>
where
Self: Sized + 'a,
{
self.boxed_local()
}
}
}

#[cfg(feature = "env-future-send")]
fn boxed_env<'a>(self) -> future::BoxFuture<'a, Self::Output>
where
Self: Sized + Send + 'a,
{
self.boxed()
#[cfg(all(feature = "env-future-send", not(target_family = "wasm")))]
/// Enabled with the feature `env-future-send` but it requires a non-wasm target!
mod conditional_types {
use futures::{future::BoxFuture, Future, FutureExt};

pub type EnvFuture<'a, T> = BoxFuture<'a, T>;

pub trait ConditionalSend: Send {}

impl<T> ConditionalSend for T where T: Send {}

pub trait EnvFutureExt: Future {
fn boxed_env<'a>(self) -> EnvFuture<'a, Self::Output>
where
Self: Sized + Send + 'a,
{
self.boxed()
}
}
}

pub type TryEnvFuture<T> = EnvFuture<Result<T, EnvError>>;
impl<T: ?Sized> EnvFutureExt for T where T: Future {}

pub type TryEnvFuture<T> = EnvFuture<'static, Result<T, EnvError>>;

pub trait Env {
fn fetch<
#[cfg(not(feature = "env-future-send"))] IN: Serialize + 'static,
#[cfg(feature = "env-future-send")] IN: Serialize + Send + 'static,
#[cfg(not(feature = "env-future-send"))] OUT: for<'de> Deserialize<'de> + 'static,
#[cfg(feature = "env-future-send")] OUT: for<'de> Deserialize<'de> + Send + 'static,
IN: Serialize + ConditionalSend + 'static,
OUT: for<'de> Deserialize<'de> + ConditionalSend + 'static,
>(
request: Request<IN>,
) -> TryEnvFuture<OUT>;
fn get_storage<
#[cfg(not(feature = "env-future-send"))] T: for<'de> Deserialize<'de> + 'static,
#[cfg(feature = "env-future-send")] T: for<'de> Deserialize<'de> + Send + 'static,
>(

fn get_storage<T: for<'de> Deserialize<'de> + ConditionalSend + 'static>(
key: &str,
) -> TryEnvFuture<Option<T>>;
fn set_storage<T: Serialize>(key: &str, value: Option<&T>) -> TryEnvFuture<()>;
fn exec_concurrent<
#[cfg(not(feature = "env-future-send"))] F: Future<Output = ()> + 'static,
#[cfg(feature = "env-future-send")] F: Future<Output = ()> + Send + 'static,
>(
future: F,
);
fn exec_sequential<
#[cfg(not(feature = "env-future-send"))] F: Future<Output = ()> + 'static,
#[cfg(feature = "env-future-send")] F: Future<Output = ()> + Send + 'static,
>(
future: F,
);
fn exec_concurrent<F: Future<Output = ()> + ConditionalSend + 'static>(future: F);
fn exec_sequential<F: Future<Output = ()> + ConditionalSend + 'static>(future: F);
fn now() -> DateTime<Utc>;
fn flush_analytics() -> EnvFuture<()>;
fn flush_analytics() -> EnvFuture<'static, ()>;
fn analytics_context(
ctx: &Ctx,
streaming_server: &StreamingServer,
Expand Down
13 changes: 7 additions & 6 deletions src/types/api/fetch_api.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::runtime::{Env, TryEnvFuture};
use crate::types::api::{APIResult, FetchRequestParams};
use crate::{
runtime::{ConditionalSend, Env, TryEnvFuture},
types::api::{APIResult, FetchRequestParams},
};

use http::Request;
use serde::{Deserialize, Serialize};

pub fn fetch_api<
E: Env,
#[cfg(not(feature = "env-future-send"))] BODY: Serialize + 'static,
#[cfg(feature = "env-future-send")] BODY: Serialize + Send + 'static,
BODY: Serialize + ConditionalSend + 'static,
REQ: FetchRequestParams<BODY> + Clone + Serialize,
#[cfg(not(feature = "env-future-send"))] RESP: for<'de> Deserialize<'de> + 'static,
#[cfg(feature = "env-future-send")] RESP: for<'de> Deserialize<'de> + Send + 'static,
RESP: for<'de> Deserialize<'de> + ConditionalSend + 'static,
>(
api_request: &REQ,
) -> TryEnvFuture<APIResult<RESP>> {
Expand Down
31 changes: 9 additions & 22 deletions src/unit_tests/env.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::models::ctx::Ctx;
use crate::models::streaming_server::StreamingServer;
use crate::runtime::{Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture};
use crate::runtime::{
ConditionalSend, Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture,
};
use chrono::{DateTime, Utc};
use enclose::enclose;
use futures::channel::mpsc::Receiver;
Expand Down Expand Up @@ -105,10 +107,8 @@ impl TestEnv {

impl Env for TestEnv {
fn fetch<
#[cfg(not(feature = "env-future-send"))] IN: Serialize + 'static,
#[cfg(feature = "env-future-send")] IN: Serialize + Send + 'static,
#[cfg(not(feature = "env-future-send"))] OUT: for<'de> Deserialize<'de> + 'static,
#[cfg(feature = "env-future-send")] OUT: for<'de> Deserialize<'de> + Send + 'static,
IN: Serialize + ConditionalSend + 'static,
OUT: for<'de> Deserialize<'de> + ConditionalSend + 'static,
>(
request: http::Request<IN>,
) -> TryEnvFuture<OUT> {
Expand All @@ -122,10 +122,7 @@ impl Env for TestEnv {
})
.boxed_env()
}
fn get_storage<
#[cfg(not(feature = "env-future-send"))] T: for<'de> Deserialize<'de> + 'static,
#[cfg(feature = "env-future-send")] T: for<'de> Deserialize<'de> + Send + 'static,
>(
fn get_storage<T: for<'de> Deserialize<'de> + ConditionalSend + 'static>(
key: &str,
) -> TryEnvFuture<Option<T>> {
future::ok(
Expand All @@ -145,26 +142,16 @@ impl Env for TestEnv {
};
future::ok(()).boxed_env()
}
fn exec_concurrent<
#[cfg(not(feature = "env-future-send"))] F: Future<Output = ()> + 'static,
#[cfg(feature = "env-future-send")] F: Future<Output = ()> + Send + 'static,
>(
future: F,
) {
fn exec_concurrent<F: Future<Output = ()> + ConditionalSend + 'static>(future: F) {
tokio_current_thread::spawn(future);
}
fn exec_sequential<
#[cfg(not(feature = "env-future-send"))] F: Future<Output = ()> + 'static,
#[cfg(feature = "env-future-send")] F: Future<Output = ()> + Send + 'static,
>(
future: F,
) {
fn exec_sequential<F: Future<Output = ()> + ConditionalSend + 'static>(future: F) {
tokio_current_thread::spawn(future);
}
fn now() -> DateTime<Utc> {
*NOW.read().unwrap()
}
fn flush_analytics() -> EnvFuture<()> {
fn flush_analytics() -> EnvFuture<'static, ()> {
future::ready(()).boxed_env()
}
fn analytics_context(
Expand Down

0 comments on commit 82b194d

Please sign in to comment.