Skip to main content

tidepool_effect/
dispatch.rs

1use crate::error::EffectError;
2use tidepool_bridge::{FromCore, ToCore};
3use tidepool_eval::value::Value;
4use tidepool_repr::DataConTable;
5use frunk::{HCons, HNil};
6
7pub struct EffectContext<'a, U = ()> {
8    table: &'a DataConTable,
9    user: &'a U,
10}
11
12impl<'a, U> EffectContext<'a, U> {
13    pub fn with_user(table: &'a DataConTable, user: &'a U) -> Self {
14        Self { table, user }
15    }
16
17    pub fn respond<T: ToCore>(&self, val: T) -> Result<Value, EffectError> {
18        val.to_value(self.table).map_err(EffectError::Bridge)
19    }
20
21    pub fn table(&self) -> &DataConTable {
22        self.table
23    }
24
25    pub fn user(&self) -> &U {
26        self.user
27    }
28}
29
30pub trait EffectHandler<U = ()> {
31    type Request: FromCore;
32    fn handle(
33        &mut self,
34        req: Self::Request,
35        cx: &EffectContext<'_, U>,
36    ) -> Result<Value, EffectError>;
37}
38
39pub trait DispatchEffect<U = ()> {
40    fn dispatch(
41        &mut self,
42        tag: u64,
43        request: &Value,
44        cx: &EffectContext<'_, U>,
45    ) -> Result<Value, EffectError>;
46}
47
48impl<U> DispatchEffect<U> for HNil {
49    fn dispatch(
50        &mut self,
51        tag: u64,
52        _request: &Value,
53        _cx: &EffectContext<'_, U>,
54    ) -> Result<Value, EffectError> {
55        Err(EffectError::UnhandledEffect { tag })
56    }
57}
58
59impl<U, H: EffectHandler<U>, T: DispatchEffect<U>> DispatchEffect<U> for HCons<H, T> {
60    fn dispatch(
61        &mut self,
62        tag: u64,
63        request: &Value,
64        cx: &EffectContext<'_, U>,
65    ) -> Result<Value, EffectError> {
66        if tag == 0 {
67            let req = H::Request::from_value(request, cx.table())?;
68            self.head.handle(req, cx)
69        } else {
70            self.tail.dispatch(tag - 1, request, cx)
71        }
72    }
73}
74
75// --- Async effect handling ---
76
77#[cfg(feature = "async")]
78pub trait AsyncEffectHandler<U = ()>: Send {
79    type Request: FromCore + Send;
80    fn handle<'a>(
81        &'a mut self,
82        req: Self::Request,
83        cx: &'a EffectContext<'a, U>,
84    ) -> impl std::future::Future<Output = Result<Value, EffectError>> + Send + 'a;
85}
86
87#[cfg(feature = "async")]
88pub trait AsyncDispatchEffect<U = ()>: Send {
89    fn dispatch<'a>(
90        &'a mut self,
91        tag: u64,
92        request: &'a Value,
93        cx: &'a EffectContext<'a, U>,
94    ) -> impl std::future::Future<Output = Result<Value, EffectError>> + Send + 'a;
95}
96
97#[cfg(feature = "async")]
98impl<U: Sync> AsyncDispatchEffect<U> for HNil {
99    fn dispatch<'a>(
100        &'a mut self,
101        tag: u64,
102        _request: &'a Value,
103        _cx: &'a EffectContext<'a, U>,
104    ) -> impl std::future::Future<Output = Result<Value, EffectError>> + Send + 'a {
105        async move { Err(EffectError::UnhandledEffect { tag }) }
106    }
107}
108
109#[cfg(feature = "async")]
110impl<U, H, T> AsyncDispatchEffect<U> for HCons<H, T>
111where
112    U: Sync,
113    H: AsyncEffectHandler<U> + Send,
114    T: AsyncDispatchEffect<U> + Send,
115{
116    fn dispatch<'a>(
117        &'a mut self,
118        tag: u64,
119        request: &'a Value,
120        cx: &'a EffectContext<'a, U>,
121    ) -> impl std::future::Future<Output = Result<Value, EffectError>> + Send + 'a {
122        async move {
123            if tag == 0 {
124                let req = H::Request::from_value(request, cx.table())?;
125                self.head.handle(req, cx).await
126            } else {
127                self.tail.dispatch(tag - 1, request, cx).await
128            }
129        }
130    }
131}