1use std::future::Future;
14use std::marker::PhantomData;
15use std::pin::Pin;
16use std::sync::{Arc, Mutex};
17use std::time::Duration;
18
19use tokio_util::sync::CancellationToken;
20
21use crate::exit::Exit;
22
23pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct TraceEvent {
27 pub name: String,
28 pub outcome: &'static str,
29}
30
31pub struct RunCtx<S> {
32 pub services: Arc<S>,
33 pub cancel: CancellationToken,
34 trace: Arc<Mutex<Vec<TraceEvent>>>,
35}
36
37impl<S> Clone for RunCtx<S> {
38 fn clone(&self) -> Self {
39 Self {
40 services: Arc::clone(&self.services),
41 cancel: self.cancel.clone(),
42 trace: Arc::clone(&self.trace),
43 }
44 }
45}
46
47impl<S> RunCtx<S> {
48 pub fn child(&self) -> Self {
49 Self {
50 services: Arc::clone(&self.services),
51 cancel: self.cancel.child_token(),
52 trace: Arc::clone(&self.trace),
53 }
54 }
55
56 fn record(&self, name: impl Into<String>, outcome: &'static str) {
57 if let Ok(mut events) = self.trace.lock() {
58 events.push(TraceEvent {
59 name: name.into(),
60 outcome,
61 });
62 }
63 }
64}
65
66pub struct Effect<A, E, S> {
69 run: Arc<dyn Fn(RunCtx<S>) -> BoxFuture<Result<A, Exit<E>>> + Send + Sync>,
70 _marker: PhantomData<fn() -> (A, E)>,
71}
72
73impl<A, E, S> Clone for Effect<A, E, S> {
74 fn clone(&self) -> Self {
75 Self {
76 run: Arc::clone(&self.run),
77 _marker: PhantomData,
78 }
79 }
80}
81
82impl<A, E, S> Effect<A, E, S>
83where
84 A: Send + Sync + 'static,
85 E: Send + Sync + 'static,
86 S: Send + Sync + 'static,
87{
88 fn new<F>(run: F) -> Self
89 where
90 F: Fn(RunCtx<S>) -> BoxFuture<Result<A, Exit<E>>> + Send + Sync + 'static,
91 {
92 Self {
93 run: Arc::new(run),
94 _marker: PhantomData,
95 }
96 }
97
98 pub fn succeed(value: A) -> Self
99 where
100 A: Clone,
101 {
102 let value = Arc::new(value);
103 Self::new(move |_ctx| {
104 let value = Arc::clone(&value);
105 Box::pin(async move { Ok((*value).clone()) })
106 })
107 }
108
109 pub fn fail(error: E) -> Self
110 where
111 E: Clone,
112 {
113 let error = Arc::new(error);
114 Self::new(move |_ctx| {
115 let error = Arc::clone(&error);
116 Box::pin(async move { Err(Exit::Fail((*error).clone())) })
117 })
118 }
119
120 pub fn die(message: impl Into<String>) -> Self {
121 let message = Arc::new(message.into());
122 Self::new(move |_ctx| {
123 let message = Arc::clone(&message);
124 Box::pin(async move { Err(Exit::Die((*message).clone())) })
125 })
126 }
127
128 pub fn from_async<F, Fut>(f: F) -> Self
131 where
132 F: Fn(Arc<S>, CancellationToken) -> Fut + Send + Sync + 'static,
133 Fut: Future<Output = Result<A, Exit<E>>> + Send + 'static,
134 {
135 let f = Arc::new(f);
136 Self::new(move |ctx| {
137 if ctx.cancel.is_cancelled() {
138 return Box::pin(async { Err(Exit::Interrupt) });
139 }
140 let fut = f(Arc::clone(&ctx.services), ctx.cancel.clone());
141 Box::pin(fut)
142 })
143 }
144
145 pub fn map<B, F>(&self, f: F) -> Effect<B, E, S>
146 where
147 B: Send + Sync + 'static,
148 F: Fn(A) -> B + Send + Sync + 'static,
149 {
150 let run = Arc::clone(&self.run);
151 let f = Arc::new(f);
152 Effect::new(move |ctx| {
153 let run = Arc::clone(&run);
154 let f = Arc::clone(&f);
155 Box::pin(async move { run(ctx).await.map(|value| f(value)) })
156 })
157 }
158
159 pub fn and_then<B, F>(&self, f: F) -> Effect<B, E, S>
160 where
161 B: Send + Sync + 'static,
162 F: Fn(A) -> Effect<B, E, S> + Send + Sync + 'static,
163 {
164 let run = Arc::clone(&self.run);
165 let f = Arc::new(f);
166 Effect::new(move |ctx| {
167 let run = Arc::clone(&run);
168 let f = Arc::clone(&f);
169 Box::pin(async move {
170 match run(ctx.clone()).await {
171 Ok(value) => f(value).run_in(ctx).await,
172 Err(error) => Err(error),
173 }
174 })
175 })
176 }
177
178 pub fn catch_fail<F>(&self, f: F) -> Self
180 where
181 F: Fn(E) -> Effect<A, E, S> + Send + Sync + 'static,
182 {
183 let run = Arc::clone(&self.run);
184 let f = Arc::new(f);
185 Self::new(move |ctx| {
186 let run = Arc::clone(&run);
187 let f = Arc::clone(&f);
188 Box::pin(async move {
189 match run(ctx.clone()).await {
190 Err(Exit::Fail(error)) => f(error).run_in(ctx).await,
191 other => other,
192 }
193 })
194 })
195 }
196
197 pub fn retry(&self, schedule: Schedule) -> Self
199 where
200 E: Clone,
201 {
202 let run = Arc::clone(&self.run);
203 Self::new(move |ctx| {
204 let run = Arc::clone(&run);
205 let mut schedule = schedule;
206 Box::pin(async move {
207 loop {
208 if ctx.cancel.is_cancelled() {
209 return Err(Exit::Interrupt);
210 }
211 match run(ctx.clone()).await {
212 Ok(value) => return Ok(value),
213 Err(Exit::Fail(_error)) if schedule.remaining > 0 => {
214 schedule.remaining -= 1;
215 ctx.record("retry", "fail");
216 tokio::select! {
217 _ = ctx.cancel.cancelled() => return Err(Exit::Interrupt),
218 _ = tokio::time::sleep(schedule.delay) => {}
219 }
220 }
221 Err(error) => return Err(error),
222 }
223 }
224 })
225 })
226 }
227
228 pub fn timeout(&self, duration: Duration) -> Self {
229 let run = Arc::clone(&self.run);
230 Self::new(move |ctx| {
231 let run = Arc::clone(&run);
232 let child = ctx.child();
233 Box::pin(async move {
234 let child_cancel = child.cancel.clone();
235 let fut = run(child);
236 match tokio::time::timeout(duration, fut).await {
237 Ok(result) => result,
238 Err(_elapsed) => {
239 child_cancel.cancel();
240 Err(Exit::Interrupt)
241 }
242 }
243 })
244 })
245 }
246
247 pub fn with_span(&self, name: impl Into<String>) -> Self {
248 let name = name.into();
249 let run = Arc::clone(&self.run);
250 Self::new(move |ctx| {
251 let run = Arc::clone(&run);
252 let name = name.clone();
253 let fut = run(ctx.clone());
254 Box::pin(async move {
255 let result = fut.await;
256 let outcome = match &result {
257 Ok(_) => "ok",
258 Err(Exit::Fail(_)) => "fail",
259 Err(Exit::Die(_)) => "die",
260 Err(Exit::Interrupt) => "interrupt",
261 };
262 ctx.record(name, outcome);
263 result
264 })
265 })
266 }
267
268 pub fn zip_par<B>(&self, right: &Effect<B, E, S>) -> Effect<(A, B), E, S>
271 where
272 B: Send + Sync + 'static,
273 {
274 let left_run = Arc::clone(&self.run);
275 let right_run = Arc::clone(&right.run);
276 Effect::new(move |ctx| {
277 let left_run = Arc::clone(&left_run);
278 let right_run = Arc::clone(&right_run);
279 let left_ctx = ctx.child();
280 let right_ctx = ctx.child();
281 Box::pin(async move {
282 let left = left_run(left_ctx.clone());
283 let right = right_run(right_ctx.clone());
284 tokio::pin!(left, right);
285 tokio::select! {
286 left_result = &mut left => match left_result {
287 Ok(left_value) => match right.await {
288 Ok(right_value) => Ok((left_value, right_value)),
289 Err(error) => Err(error),
290 },
291 Err(error) => {
292 right_ctx.cancel.cancel();
293 let _ = right.await;
294 Err(error)
295 }
296 },
297 right_result = &mut right => match right_result {
298 Ok(right_value) => match left.await {
299 Ok(left_value) => Ok((left_value, right_value)),
300 Err(error) => Err(error),
301 },
302 Err(error) => {
303 left_ctx.cancel.cancel();
304 let _ = left.await;
305 Err(error)
306 }
307 },
308 }
309 })
310 })
311 }
312
313 pub fn race(&self, other: &Self) -> Self {
315 let left_run = Arc::clone(&self.run);
316 let right_run = Arc::clone(&other.run);
317 Self::new(move |ctx| {
318 let left_run = Arc::clone(&left_run);
319 let right_run = Arc::clone(&right_run);
320 let left_ctx = ctx.child();
321 let right_ctx = ctx.child();
322 Box::pin(async move {
323 let left = left_run(left_ctx.clone());
324 let right = right_run(right_ctx.clone());
325 tokio::pin!(left, right);
326 tokio::select! {
327 left_result = &mut left => {
328 right_ctx.cancel.cancel();
329 let _ = right.await;
330 left_result
331 }
332 right_result = &mut right => {
333 left_ctx.cancel.cancel();
334 let _ = left.await;
335 right_result
336 }
337 }
338 })
339 })
340 }
341
342 pub fn bracket<B, F, R>(&self, release: R, body: F) -> Effect<B, E, S>
345 where
346 A: Clone,
347 B: Send + Sync + 'static,
348 F: Fn(A) -> Effect<B, E, S> + Send + Sync + 'static,
349 R: Fn(A) + Send + Sync + 'static,
350 {
351 let acquire = Arc::clone(&self.run);
352 let release: Arc<dyn Fn(A) + Send + Sync> = Arc::new(release);
353 let body = Arc::new(body);
354 Effect::new(move |ctx| {
355 let acquire = Arc::clone(&acquire);
356 let release = Arc::clone(&release);
357 let body = Arc::clone(&body);
358 Box::pin(async move {
359 let resource = match acquire(ctx.clone()).await {
360 Ok(resource) => resource,
361 Err(error) => return Err(error),
362 };
363 let guard = ReleaseOnce::new(resource.clone(), Arc::clone(&release));
364 let result = body(resource).run_in(ctx).await;
365 guard.release_now();
366 result
367 })
368 })
369 }
370
371 pub fn provide<S2>(self, services: S) -> Effect<A, E, S2>
374 where
375 S2: Send + Sync + 'static,
376 {
377 let services = Arc::new(services);
378 let run = self.run;
379 Effect::new(move |ctx| {
380 let run = Arc::clone(&run);
381 let services = Arc::clone(&services);
382 let ctx = RunCtx {
383 services,
384 cancel: ctx.cancel,
385 trace: ctx.trace,
386 };
387 run(ctx)
388 })
389 }
390
391 pub async fn run(self, services: Arc<S>) -> (Result<A, Exit<E>>, Vec<TraceEvent>) {
392 let trace = Arc::new(Mutex::new(Vec::new()));
393 let ctx = RunCtx {
394 services,
395 cancel: CancellationToken::new(),
396 trace: Arc::clone(&trace),
397 };
398 let result = self.run_in(ctx).await;
399 let events = trace
400 .lock()
401 .map(|events| events.clone())
402 .unwrap_or_default();
403 (result, events)
404 }
405
406 pub(crate) async fn run_in(&self, ctx: RunCtx<S>) -> Result<A, Exit<E>> {
407 (self.run)(ctx).await
408 }
409}
410
411#[derive(Debug, Clone, Copy)]
412pub struct Schedule {
413 pub remaining: u32,
414 pub delay: Duration,
415}
416
417struct ReleaseOnce<T> {
418 value: Mutex<Option<T>>,
419 release: Arc<dyn Fn(T) + Send + Sync>,
420}
421
422impl<T> ReleaseOnce<T> {
423 fn new(value: T, release: Arc<dyn Fn(T) + Send + Sync>) -> Self {
424 Self {
425 value: Mutex::new(Some(value)),
426 release,
427 }
428 }
429
430 fn release_now(&self) {
431 if let Ok(mut slot) = self.value.lock() {
432 if let Some(value) = slot.take() {
433 (self.release)(value);
434 }
435 }
436 }
437}
438
439impl<T> Drop for ReleaseOnce<T> {
440 fn drop(&mut self) {
441 self.release_now();
442 }
443}