use super::Effectus;
use core::future::Future;
pub trait EffectusHandler<E: Effectus> {
type Output;
fn handle(&self, effect: E) -> Self::Output;
}
pub trait EffectusHandlerAsync<E: Effectus> {
type Output;
fn handle_async(&self, effect: E) -> impl Future<Output = Self::Output> + Send;
}
#[inline]
pub fn run_effectus<E, H, A, F>(handler: &H, _effect: E, computation: F) -> A
where
E: Effectus,
H: EffectusHandler<E>,
F: FnOnce(&H) -> A,
{
computation(handler)
}
pub async fn run_effectus_async<E, H, A, F, Fut>(handler: &H, _effect: E, computation: F) -> A
where
E: Effectus,
H: EffectusHandlerAsync<E>,
F: FnOnce(&H) -> Fut,
Fut: Future<Output = A>,
{
computation(handler).await
}
#[derive(Debug, Clone, Copy, Default)]
pub struct UnitHandler;
impl<E: Effectus> EffectusHandler<E> for UnitHandler {
type Output = ();
#[inline]
fn handle(&self, _effect: E) -> Self::Output {}
}
impl<E: Effectus> EffectusHandlerAsync<E> for UnitHandler {
type Output = ();
async fn handle_async(&self, _effect: E) {}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PanicHandler;
impl<E: Effectus> EffectusHandler<E> for PanicHandler {
type Output = core::convert::Infallible;
fn handle(&self, _effect: E) -> Self::Output {
panic!("Effect handler not implemented")
}
}
#[derive(Debug, Clone, Copy)]
pub struct ComposedHandler<H1, H2> {
handler1: H1,
handler2: H2,
}
impl<H1, H2> ComposedHandler<H1, H2> {
#[inline]
pub const fn new(handler1: H1, handler2: H2) -> Self {
ComposedHandler { handler1, handler2 }
}
#[inline]
pub fn first(&self) -> &H1 {
&self.handler1
}
#[inline]
pub fn second(&self) -> &H2 {
&self.handler2
}
}
pub trait EffectusHandlerExt: Sized {
#[inline]
fn compose<H2>(self, other: H2) -> ComposedHandler<Self, H2> {
ComposedHandler::new(self, other)
}
}
impl<H> EffectusHandlerExt for H {}
#[cfg(test)]
mod tests {
use super::*;
struct TestEffect;
impl Effectus for TestEffect {}
struct TestHandler;
impl EffectusHandler<TestEffect> for TestHandler {
type Output = i32;
fn handle(&self, _: TestEffect) -> i32 {
42
}
}
#[test]
fn test_effectus_handler() {
let handler = TestHandler;
let result = handler.handle(TestEffect);
assert_eq!(result, 42);
}
#[test]
fn test_run_effectus() {
let handler = TestHandler;
let result = run_effectus(&handler, TestEffect, |h| h.handle(TestEffect));
assert_eq!(result, 42);
}
#[test]
fn test_unit_handler() {
let handler = UnitHandler;
handler.handle(TestEffect);
assert_eq!((), ());
}
#[test]
fn test_composed_handler() {
let h1 = TestHandler;
let h2 = UnitHandler;
let composed = h1.compose(h2);
assert_eq!(composed.first().handle(TestEffect), 42);
assert_eq!(composed.second().handle(TestEffect), ());
}
}