beekeeper/bee/stock/
thunk.rs

1use crate::bee::{ApplyError, Context, Worker, WorkerResult};
2use crate::boxed::BoxedFnOnce;
3use crate::panic::Panic;
4use derive_more::Debug;
5use std::marker::PhantomData;
6use std::{any, fmt};
7
8/// A `Worker` that executes infallible `Thunk<T>`s when applied.
9#[derive(Debug)]
10#[debug("ThunkWorker<{}>", any::type_name::<T>())]
11pub struct ThunkWorker<T>(PhantomData<T>);
12
13impl<T> Default for ThunkWorker<T> {
14    fn default() -> Self {
15        Self(PhantomData)
16    }
17}
18
19impl<T> Clone for ThunkWorker<T> {
20    fn clone(&self) -> Self {
21        Self::default()
22    }
23}
24
25impl<T: Send + fmt::Debug + 'static> Worker for ThunkWorker<T> {
26    type Input = Thunk<T>;
27    type Output = T;
28    type Error = ();
29
30    #[inline]
31    fn apply(&mut self, f: Self::Input, _: &Context<Self::Input>) -> WorkerResult<Self> {
32        Ok(f.0.call_box())
33    }
34}
35
36/// A `Worker` that executes fallible `Thunk<Result<T, E>>`s when applied.
37#[derive(Debug)]
38#[debug("FunkWorker<{}, {}>", any::type_name::<T>(), any::type_name::<E>())]
39pub struct FunkWorker<T, E>(PhantomData<T>, PhantomData<E>);
40
41impl<T, E> Default for FunkWorker<T, E> {
42    fn default() -> Self {
43        Self(PhantomData, PhantomData)
44    }
45}
46
47impl<T, E> Clone for FunkWorker<T, E> {
48    fn clone(&self) -> Self {
49        Self::default()
50    }
51}
52
53impl<T, E> Worker for FunkWorker<T, E>
54where
55    T: Send + fmt::Debug + 'static,
56    E: Send + fmt::Debug + 'static,
57{
58    type Input = Thunk<Result<T, E>>;
59    type Output = T;
60    type Error = E;
61
62    #[inline]
63    fn apply(&mut self, f: Self::Input, _: &Context<Self::Input>) -> WorkerResult<Self> {
64        f.0.call_box()
65            .map_err(|error| ApplyError::Fatal { error, input: None })
66    }
67}
68
69/// A `Worker` that executes `Thunk<T>`s that may panic. A panic is caught and returned as an
70/// `ApplyError::Panic` error.
71#[derive(Debug)]
72#[debug("PunkWorker<{}>", any::type_name::<T>())]
73pub struct PunkWorker<T>(PhantomData<T>);
74
75impl<T> Default for PunkWorker<T> {
76    fn default() -> Self {
77        Self(PhantomData)
78    }
79}
80
81impl<T> Clone for PunkWorker<T> {
82    fn clone(&self) -> Self {
83        Self::default()
84    }
85}
86
87impl<T: Send + fmt::Debug + 'static> Worker for PunkWorker<T> {
88    type Input = Thunk<T>;
89    type Output = T;
90    type Error = ();
91
92    fn apply(&mut self, f: Self::Input, _: &Context<Self::Input>) -> WorkerResult<Self> {
93        Panic::try_call_boxed(None, f.0).map_err(|payload| ApplyError::Panic {
94            input: None,
95            payload,
96        })
97    }
98}
99
100/// A wrapper around a closure that can be executed exactly once by a worker in a `Hive`.
101#[derive(Debug)]
102#[debug("Thunk<{}>", any::type_name::<T>())]
103pub struct Thunk<T>(Box<dyn BoxedFnOnce<Output = T> + Send>);
104
105impl<T, F: FnOnce() -> T + Send + 'static> From<F> for Thunk<T> {
106    fn from(f: F) -> Self {
107        Self(Box::new(f))
108    }
109}
110
111impl<T, E> Thunk<Result<T, E>> {
112    pub fn fallible<F: FnOnce() -> Result<T, E> + Send + 'static>(f: F) -> Self {
113        Self(Box::new(f))
114    }
115}
116
117#[cfg(test)]
118#[cfg_attr(coverage_nightly, coverage(off))]
119mod tests {
120    use super::*;
121    use crate::bee::Context;
122
123    #[test]
124    fn test_thunk() {
125        let mut worker = ThunkWorker::<u8>::default();
126        let thunk = Thunk::from(|| 5);
127        assert_eq!(5, worker.apply(thunk, &Context::empty()).unwrap());
128    }
129
130    #[test]
131    fn test_funk_ok() {
132        let mut worker = FunkWorker::<u8, String>::default();
133        let funk = Thunk::fallible(|| Ok(1));
134        assert_eq!(1, worker.apply(funk, &Context::empty()).unwrap())
135    }
136
137    #[test]
138    fn test_funk_error() {
139        let mut worker = FunkWorker::<u8, String>::default();
140        let funk = Thunk::fallible(|| Err("failure".into()));
141        let result = worker.apply(funk, &Context::empty());
142        let _error = String::from("failure");
143        assert!(matches!(
144            result,
145            Err(ApplyError::Fatal {
146                input: None,
147                error: _error
148            })
149        ));
150    }
151}