use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub use frunk_core::coproduct::{CNil, CoprodInjector, Coproduct};
pub use frunk_core::hlist::{HCons, HNil};
use frunk_core::indices::{Here, There};
use crate::control::{CoControl, Control};
use crate::effect::{Effect, Effects, InjectResume, MapResume, Resumes};
pub(crate) struct SendFuture<F> {
future: F,
}
impl<F> SendFuture<F>
where
F: Future + Send,
{
pub(crate) fn new(future: F) -> Self {
Self { future }
}
}
impl<F> SendFuture<F>
where
F: Future,
{
pub(crate) unsafe fn new_unchecked(future: F) -> Self {
Self { future }
}
}
unsafe impl<F> Send for SendFuture<F> {}
impl<F> Future for SendFuture<F>
where
F: Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { self.map_unchecked_mut(|this| &mut this.future) }.poll(cx)
}
}
macro_rules! declare_find_handler {
($trait_name:ident, sync, mut, $fn_bound:ident) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, CH: Effect, InjectIdx, FindIdx> {
fn call_handler(&mut self, effect: CH) -> CoControl<'a, Effs>;
}
impl<'a, Effs, CH, F, FTail, InjectIdx> $trait_name<'a, Effs, CH, InjectIdx, Here>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, InjectIdx>,
CH: Effect,
F: $fn_bound(CH) -> Control<CH::Resume<'a>>,
{
#[inline]
fn call_handler(&mut self, effect: CH) -> CoControl<'a, Effs> {
match (self.head)(effect) {
Control::Resume(r) => CoControl::Resume(Effs::inject_resume(r)),
Control::Cancel => CoControl::Cancel,
}
}
}
impl<'a, Effs, CH, FHead, FTail, InjectIdx, I>
$trait_name<'a, Effs, CH, InjectIdx, There<I>> for HCons<FHead, FTail>
where
Effs: Effects<'a>,
CH: Effect,
FTail: $trait_name<'a, Effs, CH, InjectIdx, I>,
{
#[inline]
fn call_handler(&mut self, effect: CH) -> CoControl<'a, Effs> {
self.tail.call_handler(effect)
}
}
};
($trait_name:ident, sync, with, $fn_bound:ident) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, CH: Effect, S, InjectIdx, FindIdx> {
fn call_handler(&self, state: &mut S, effect: CH) -> CoControl<'a, Effs>;
}
impl<'a, Effs, CH, F, FTail, S, InjectIdx> $trait_name<'a, Effs, CH, S, InjectIdx, Here>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, InjectIdx>,
CH: Effect,
F: $fn_bound(&mut S, CH) -> Control<CH::Resume<'a>>,
{
#[inline]
fn call_handler(&self, state: &mut S, effect: CH) -> CoControl<'a, Effs> {
match (self.head)(state, effect) {
Control::Resume(r) => CoControl::Resume(Effs::inject_resume(r)),
Control::Cancel => CoControl::Cancel,
}
}
}
impl<'a, Effs, CH, FHead, FTail, S, InjectIdx, I>
$trait_name<'a, Effs, CH, S, InjectIdx, There<I>> for HCons<FHead, FTail>
where
Effs: Effects<'a>,
CH: Effect,
FTail: $trait_name<'a, Effs, CH, S, InjectIdx, I>,
{
#[inline]
fn call_handler(&self, state: &mut S, effect: CH) -> CoControl<'a, Effs> {
self.tail.call_handler(state, effect)
}
}
};
($trait_name:ident, async, mut, $fn_bound:ident) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, CH: Effect, InjectIdx, FindIdx> {
fn call_handler(&mut self, effect: CH) -> impl Future<Output = CoControl<'a, Effs>>;
}
impl<'a, Effs, CH, F, FTail, InjectIdx> $trait_name<'a, Effs, CH, InjectIdx, Here>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, InjectIdx>,
CH: Effect,
F: $fn_bound(CH) -> Control<CH::Resume<'a>>,
{
#[inline]
async fn call_handler(&mut self, effect: CH) -> CoControl<'a, Effs> {
match (self.head)(effect).await {
Control::Resume(r) => CoControl::Resume(Effs::inject_resume(r)),
Control::Cancel => CoControl::Cancel,
}
}
}
impl<'a, Effs, CH, FHead, FTail, InjectIdx, I>
$trait_name<'a, Effs, CH, InjectIdx, There<I>> for HCons<FHead, FTail>
where
Effs: Effects<'a>,
CH: Effect,
FTail: $trait_name<'a, Effs, CH, InjectIdx, I>,
{
#[inline]
async fn call_handler(&mut self, effect: CH) -> CoControl<'a, Effs> {
self.tail.call_handler(effect).await
}
}
};
($trait_name:ident, async, with, $fn_bound:ident) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, CH: Effect, S, InjectIdx, FindIdx> {
fn call_handler(
&self,
state: &mut S,
effect: CH,
) -> impl Future<Output = CoControl<'a, Effs>>;
}
impl<'a, Effs, CH, F, FTail, S, InjectIdx> $trait_name<'a, Effs, CH, S, InjectIdx, Here>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, InjectIdx>,
CH: Effect,
F: $fn_bound(&mut S, CH) -> Control<CH::Resume<'a>>,
{
#[inline]
async fn call_handler(&self, state: &mut S, effect: CH) -> CoControl<'a, Effs> {
match (self.head)(state, effect).await {
Control::Resume(r) => CoControl::Resume(Effs::inject_resume(r)),
Control::Cancel => CoControl::Cancel,
}
}
}
impl<'a, Effs, CH, FHead, FTail, S, InjectIdx, I>
$trait_name<'a, Effs, CH, S, InjectIdx, There<I>> for HCons<FHead, FTail>
where
Effs: Effects<'a>,
CH: Effect,
FTail: $trait_name<'a, Effs, CH, S, InjectIdx, I>,
{
#[inline]
async fn call_handler(&self, state: &mut S, effect: CH) -> CoControl<'a, Effs> {
self.tail.call_handler(state, effect).await
}
}
};
}
declare_find_handler!(FindHandler, sync, mut, FnMut);
declare_find_handler!(FindHandlerWith, sync, with, Fn);
declare_find_handler!(AsyncFindHandler, async, mut, AsyncFnMut);
declare_find_handler!(AsyncFindHandlerWith, async, with, AsyncFn);
#[doc(hidden)]
pub trait SendAsyncFindHandler<'a, Effs: Effects<'a>, CH: Effect, InjectIdx, FindIdx> {
fn call_handler_send(&mut self, effect: CH)
-> impl Future<Output = CoControl<'a, Effs>> + Send;
}
impl<'a, Effs, CH, F, Fut, FTail, InjectIdx> SendAsyncFindHandler<'a, Effs, CH, InjectIdx, Here>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, InjectIdx>,
CH: Effect + Send,
F: FnMut(CH) -> Fut + Send,
Fut: Future<Output = Control<CH::Resume<'a>>> + Send,
FTail: Send,
{
#[inline]
#[allow(clippy::manual_async_fn)]
fn call_handler_send(
&mut self,
effect: CH,
) -> impl Future<Output = CoControl<'a, Effs>> + Send {
async move {
match (self.head)(effect).await {
Control::Resume(r) => CoControl::Resume(Effs::inject_resume(r)),
Control::Cancel => CoControl::Cancel,
}
}
}
}
impl<'a, Effs, CH, FHead, FTail, InjectIdx, I>
SendAsyncFindHandler<'a, Effs, CH, InjectIdx, There<I>> for HCons<FHead, FTail>
where
Effs: Effects<'a>,
CH: Effect + Send,
FHead: Send,
FTail: SendAsyncFindHandler<'a, Effs, CH, InjectIdx, I> + Send,
{
#[inline]
fn call_handler_send(
&mut self,
effect: CH,
) -> impl Future<Output = CoControl<'a, Effs>> + Send {
SendFuture::new(self.tail.call_handler_send(effect))
}
}
macro_rules! declare_handle_dispatch {
($trait_name:ident, sync, $find_trait:ident, mut) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, Handlers, Indices> {
fn handle_mut(self, handlers: &mut Handlers) -> CoControl<'a, Effs>;
}
impl<'a, Effs: Effects<'a>, Handlers> $trait_name<'a, Effs, Handlers, HNil> for CNil {
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline]
fn handle_mut(self, _: &mut Handlers) -> CoControl<'a, Effs> {
match self {}
}
}
impl<'a, Effs, CH, CTail, Handlers, InjectIdx, FindIdx, ITail>
$trait_name<'a, Effs, Handlers, HCons<(InjectIdx, FindIdx), ITail>>
for Coproduct<CH, CTail>
where
Effs: Effects<'a>,
CH: Effect,
Handlers: $find_trait<'a, Effs, CH, InjectIdx, FindIdx>,
CTail: $trait_name<'a, Effs, Handlers, ITail>,
{
#[inline]
fn handle_mut(self, handlers: &mut Handlers) -> CoControl<'a, Effs> {
match self {
Coproduct::Inl(head) => handlers.call_handler(head),
Coproduct::Inr(rest) => rest.handle_mut(handlers),
}
}
}
};
($trait_name:ident, sync, $find_trait:ident, with) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, Handlers, S, Indices> {
fn handle_with(self, state: &mut S, handlers: &Handlers) -> CoControl<'a, Effs>;
}
impl<'a, Effs: Effects<'a>, Handlers, S> $trait_name<'a, Effs, Handlers, S, HNil> for CNil {
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline]
fn handle_with(self, _: &mut S, _: &Handlers) -> CoControl<'a, Effs> {
match self {}
}
}
impl<'a, Effs, CH, CTail, Handlers, S, InjectIdx, FindIdx, ITail>
$trait_name<'a, Effs, Handlers, S, HCons<(InjectIdx, FindIdx), ITail>>
for Coproduct<CH, CTail>
where
Effs: Effects<'a>,
CH: Effect,
Handlers: $find_trait<'a, Effs, CH, S, InjectIdx, FindIdx>,
CTail: $trait_name<'a, Effs, Handlers, S, ITail>,
{
#[inline]
fn handle_with(self, state: &mut S, handlers: &Handlers) -> CoControl<'a, Effs> {
match self {
Coproduct::Inl(head) => handlers.call_handler(state, head),
Coproduct::Inr(rest) => rest.handle_with(state, handlers),
}
}
}
};
($trait_name:ident, async, $find_trait:ident, mut) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, Handlers, Indices> {
fn handle_mut(
self,
handlers: &mut Handlers,
) -> impl Future<Output = CoControl<'a, Effs>>;
}
impl<'a, Effs: Effects<'a>, Handlers> $trait_name<'a, Effs, Handlers, HNil> for CNil {
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline]
async fn handle_mut(self, _: &mut Handlers) -> CoControl<'a, Effs> {
match self {}
}
}
impl<'a, Effs, CH, CTail, Handlers, InjectIdx, FindIdx, ITail>
$trait_name<'a, Effs, Handlers, HCons<(InjectIdx, FindIdx), ITail>>
for Coproduct<CH, CTail>
where
Effs: Effects<'a>,
CH: Effect,
Handlers: $find_trait<'a, Effs, CH, InjectIdx, FindIdx>,
CTail: $trait_name<'a, Effs, Handlers, ITail>,
{
#[inline]
async fn handle_mut(self, handlers: &mut Handlers) -> CoControl<'a, Effs> {
match self {
Coproduct::Inl(head) => handlers.call_handler(head).await,
Coproduct::Inr(rest) => rest.handle_mut(handlers).await,
}
}
}
};
($trait_name:ident, async, $find_trait:ident, with) => {
#[doc(hidden)]
pub trait $trait_name<'a, Effs: Effects<'a>, Handlers, S, Indices> {
fn handle_with(
self,
state: &mut S,
handlers: &Handlers,
) -> impl Future<Output = CoControl<'a, Effs>>;
}
impl<'a, Effs: Effects<'a>, Handlers, S> $trait_name<'a, Effs, Handlers, S, HNil> for CNil {
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline]
async fn handle_with(self, _: &mut S, _: &Handlers) -> CoControl<'a, Effs> {
match self {}
}
}
impl<'a, Effs, CH, CTail, Handlers, S, InjectIdx, FindIdx, ITail>
$trait_name<'a, Effs, Handlers, S, HCons<(InjectIdx, FindIdx), ITail>>
for Coproduct<CH, CTail>
where
Effs: Effects<'a>,
CH: Effect,
Handlers: $find_trait<'a, Effs, CH, S, InjectIdx, FindIdx>,
CTail: $trait_name<'a, Effs, Handlers, S, ITail>,
{
#[inline]
async fn handle_with(self, state: &mut S, handlers: &Handlers) -> CoControl<'a, Effs> {
match self {
Coproduct::Inl(head) => handlers.call_handler(state, head).await,
Coproduct::Inr(rest) => rest.handle_with(state, handlers).await,
}
}
}
};
}
declare_handle_dispatch!(HandleMut, sync, FindHandler, mut);
declare_handle_dispatch!(HandleWith, sync, FindHandlerWith, with);
declare_handle_dispatch!(AsyncHandleMut, async, AsyncFindHandler, mut);
declare_handle_dispatch!(AsyncHandleWith, async, AsyncFindHandlerWith, with);
#[doc(hidden)]
pub trait SendAsyncHandleMut<'a, Effs: Effects<'a>, Handlers, Indices> {
fn handle_mut_send(
self,
handlers: &mut Handlers,
) -> impl Future<Output = CoControl<'a, Effs>> + Send;
}
impl<'a, Effs, Handlers> SendAsyncHandleMut<'a, Effs, Handlers, HNil> for CNil
where
Effs: Effects<'a>,
Handlers: Send,
{
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline]
#[allow(clippy::manual_async_fn)]
fn handle_mut_send(self, _: &mut Handlers) -> impl Future<Output = CoControl<'a, Effs>> + Send {
async move { match self {} }
}
}
impl<'a, Effs, CH, CTail, Handlers, InjectIdx, FindIdx, ITail>
SendAsyncHandleMut<'a, Effs, Handlers, HCons<(InjectIdx, FindIdx), ITail>>
for Coproduct<CH, CTail>
where
Effs: Effects<'a>,
CH: Effect + Send,
CTail: SendAsyncHandleMut<'a, Effs, Handlers, ITail> + Send,
Handlers: SendAsyncFindHandler<'a, Effs, CH, InjectIdx, FindIdx> + Send,
{
#[inline]
fn handle_mut_send(
self,
handlers: &mut Handlers,
) -> impl Future<Output = CoControl<'a, Effs>> + Send {
let future = async move {
match self {
Coproduct::Inl(head) => handlers.call_handler_send(head).await,
Coproduct::Inr(rest) => rest.handle_mut_send(handlers).await,
}
};
unsafe { SendFuture::new_unchecked(future) }
}
}
#[doc(hidden)]
pub trait HandlersToEffects<'a, Effs: Effects<'a>, Indices> {
type Effects;
}
impl<'a, Effs: Effects<'a>> HandlersToEffects<'a, Effs, HNil> for HNil {
type Effects = CNil;
}
#[doc(hidden)]
pub struct SyncHandler;
impl<'a, Effs, F, FTail, CH, Index, ITail>
HandlersToEffects<'a, Effs, HCons<(CH, Index, SyncHandler), ITail>> for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, Index>,
CH: Effect,
F: FnMut(CH) -> Control<CH::Resume<'a>>,
FTail: HandlersToEffects<'a, Effs, ITail>,
{
type Effects = Coproduct<CH, <FTail as HandlersToEffects<'a, Effs, ITail>>::Effects>;
}
#[doc(hidden)]
pub struct SyncStatefulHandler<S>(std::marker::PhantomData<S>);
impl<'a, Effs, F, FTail, CH, S, Index, ITail>
HandlersToEffects<'a, Effs, HCons<(CH, Index, SyncStatefulHandler<S>), ITail>>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, Index>,
CH: Effect,
F: Fn(&mut S, CH) -> Control<CH::Resume<'a>>,
FTail: HandlersToEffects<'a, Effs, ITail>,
{
type Effects = Coproduct<CH, <FTail as HandlersToEffects<'a, Effs, ITail>>::Effects>;
}
#[doc(hidden)]
pub struct AsyncHandler;
impl<'a, Effs, F, FTail, CH, Index, ITail>
HandlersToEffects<'a, Effs, HCons<(CH, Index, AsyncHandler), ITail>> for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, Index>,
CH: Effect,
F: AsyncFnMut(CH) -> Control<CH::Resume<'a>>,
FTail: HandlersToEffects<'a, Effs, ITail>,
{
type Effects = Coproduct<CH, <FTail as HandlersToEffects<'a, Effs, ITail>>::Effects>;
}
#[doc(hidden)]
pub struct AsyncStatefulHandler<S>(std::marker::PhantomData<S>);
impl<'a, Effs, F, FTail, CH, S, Index, ITail>
HandlersToEffects<'a, Effs, HCons<(CH, Index, AsyncStatefulHandler<S>), ITail>>
for HCons<F, FTail>
where
Effs: Effects<'a> + InjectResume<'a, CH, Index>,
CH: Effect,
F: AsyncFn(&mut S, CH) -> Control<CH::Resume<'a>>,
FTail: HandlersToEffects<'a, Effs, ITail>,
{
type Effects = Coproduct<CH, <FTail as HandlersToEffects<'a, Effs, ITail>>::Effects>;
}
#[doc(hidden)]
pub trait EmbedEffect<OuterEffs, Indices> {
fn embed(self) -> OuterEffs;
}
impl<OuterEffs> EmbedEffect<OuterEffs, HNil> for CNil {
#[cfg_attr(coverage_nightly, coverage(off))]
fn embed(self) -> OuterEffs {
match self {}
}
}
impl<OuterEffs, E, Tail, OuterIdx, TailIndices> EmbedEffect<OuterEffs, HCons<OuterIdx, TailIndices>>
for Coproduct<E, Tail>
where
OuterEffs: CoprodInjector<E, OuterIdx>,
Tail: EmbedEffect<OuterEffs, TailIndices>,
{
#[inline]
fn embed(self) -> OuterEffs {
match self {
Coproduct::Inl(effect) => OuterEffs::inject(effect),
Coproduct::Inr(tail) => tail.embed(),
}
}
}
#[doc(hidden)]
pub trait ProjectResume<'a, SubEffs: MapResume, Indices> {
fn project(self) -> Resumes<'a, SubEffs>;
}
impl<'a, OuterResumes> ProjectResume<'a, CNil, HNil> for OuterResumes {
#[cfg_attr(coverage_nightly, coverage(off))]
fn project(self) -> Resumes<'a, CNil> {
panic!("ProjectResume: no sub-effect matched — handler resumed at an unexpected index")
}
}
#[doc(hidden)]
pub trait CoprodAt<T, Idx> {
fn at(self) -> Result<T, Self>
where
Self: Sized;
}
impl<T, Tail> CoprodAt<T, Here> for Coproduct<T, Tail> {
#[inline]
fn at(self) -> Result<T, Self> {
match self {
Coproduct::Inl(val) => Ok(val),
Coproduct::Inr(_) => Err(self),
}
}
}
impl<Head, Tail, T, TailIdx> CoprodAt<T, There<TailIdx>> for Coproduct<Head, Tail>
where
Tail: CoprodAt<T, TailIdx>,
{
#[inline]
fn at(self) -> Result<T, Self> {
match self {
Coproduct::Inl(_) => Err(self),
Coproduct::Inr(tail) => match tail.at() {
Ok(val) => Ok(val),
Err(tail) => Err(Coproduct::Inr(tail)),
},
}
}
}
impl<'a, OuterResumes, E, SubTail, OuterIdx, TailIndices>
ProjectResume<'a, Coproduct<E, SubTail>, HCons<OuterIdx, TailIndices>> for OuterResumes
where
E: Effect,
SubTail: MapResume,
OuterResumes: CoprodAt<E::Resume<'a>, OuterIdx>,
OuterResumes: ProjectResume<'a, SubTail, TailIndices>,
{
#[inline]
fn project(self) -> Resumes<'a, Coproduct<E, SubTail>> {
match self.at() {
Ok(resume) => Coproduct::Inl(resume),
Err(remainder) => Coproduct::Inr(remainder.project()),
}
}
}