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
66type EffectProgram<A, E, S> = dyn Fn(RunCtx<S>) -> BoxFuture<Result<A, Exit<E>>> + Send + Sync;
69
70pub struct Effect<A, E, S> {
71 run: Arc<EffectProgram<A, E, S>>,
72 _marker: PhantomData<fn() -> (A, E)>,
73}
74
75impl<A, E, S> Clone for Effect<A, E, S> {
76 fn clone(&self) -> Self {
77 Self {
78 run: Arc::clone(&self.run),
79 _marker: PhantomData,
80 }
81 }
82}
83
84impl<A, E, S> Effect<A, E, S>
85where
86 A: Send + Sync + 'static,
87 E: Send + Sync + 'static,
88 S: Send + Sync + 'static,
89{
90 fn new<F>(run: F) -> Self
91 where
92 F: Fn(RunCtx<S>) -> BoxFuture<Result<A, Exit<E>>> + Send + Sync + 'static,
93 {
94 Self {
95 run: Arc::new(run),
96 _marker: PhantomData,
97 }
98 }
99
100 pub fn succeed(value: A) -> Self
101 where
102 A: Clone,
103 {
104 let value = Arc::new(value);
105 Self::new(move |_ctx| {
106 let value = Arc::clone(&value);
107 Box::pin(async move { Ok((*value).clone()) })
108 })
109 }
110
111 pub fn fail(error: E) -> Self
112 where
113 E: Clone,
114 {
115 let error = Arc::new(error);
116 Self::new(move |_ctx| {
117 let error = Arc::clone(&error);
118 Box::pin(async move { Err(Exit::Fail((*error).clone())) })
119 })
120 }
121
122 pub fn die(message: impl Into<String>) -> Self {
123 let message = Arc::new(message.into());
124 Self::new(move |_ctx| {
125 let message = Arc::clone(&message);
126 Box::pin(async move { Err(Exit::Die((*message).clone())) })
127 })
128 }
129
130 pub fn from_async<F, Fut>(f: F) -> Self
133 where
134 F: Fn(Arc<S>, CancellationToken) -> Fut + Send + Sync + 'static,
135 Fut: Future<Output = Result<A, Exit<E>>> + Send + 'static,
136 {
137 let f = Arc::new(f);
138 Self::new(move |ctx| {
139 if ctx.cancel.is_cancelled() {
140 return Box::pin(async { Err(Exit::Interrupt) });
141 }
142 let fut = f(Arc::clone(&ctx.services), ctx.cancel.clone());
143 Box::pin(fut)
144 })
145 }
146
147 pub fn from_async_observed<F, Fut>(f: F) -> Self
153 where
154 F: Fn(Arc<S>, CancellationToken) -> Fut + Send + Sync + 'static,
155 Fut: Future<Output = Result<A, Exit<E>>> + Send + 'static,
156 {
157 let f = Arc::new(f);
158 Self::new(move |ctx| {
159 let fut = f(Arc::clone(&ctx.services), ctx.cancel.clone());
160 Box::pin(fut)
161 })
162 }
163
164 pub fn map<B, F>(&self, f: F) -> Effect<B, E, S>
165 where
166 B: Send + Sync + 'static,
167 F: Fn(A) -> B + Send + Sync + 'static,
168 {
169 let run = Arc::clone(&self.run);
170 let f = Arc::new(f);
171 Effect::new(move |ctx| {
172 let run = Arc::clone(&run);
173 let f = Arc::clone(&f);
174 Box::pin(async move { run(ctx).await.map(|value| f(value)) })
175 })
176 }
177
178 pub fn and_then<B, F>(&self, f: F) -> Effect<B, E, S>
179 where
180 B: Send + Sync + 'static,
181 F: Fn(A) -> Effect<B, E, S> + Send + Sync + 'static,
182 {
183 let run = Arc::clone(&self.run);
184 let f = Arc::new(f);
185 Effect::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 Ok(value) => f(value).run_in(ctx).await,
191 Err(error) => Err(error),
192 }
193 })
194 })
195 }
196
197 pub fn catch_fail<F>(&self, f: F) -> Self
199 where
200 F: Fn(E) -> Effect<A, E, S> + Send + Sync + 'static,
201 {
202 let run = Arc::clone(&self.run);
203 let f = Arc::new(f);
204 Self::new(move |ctx| {
205 let run = Arc::clone(&run);
206 let f = Arc::clone(&f);
207 Box::pin(async move {
208 match run(ctx.clone()).await {
209 Err(Exit::Fail(error)) => f(error).run_in(ctx).await,
210 other => other,
211 }
212 })
213 })
214 }
215
216 pub fn retry(&self, schedule: Schedule) -> Self
224 where
225 E: Clone,
226 {
227 let run = Arc::clone(&self.run);
228 Self::new(move |ctx| {
229 let run = Arc::clone(&run);
230 let mut schedule = schedule;
231 Box::pin(async move {
232 loop {
233 match run(ctx.clone()).await {
234 Ok(value) => return Ok(value),
235 Err(Exit::Fail(_error)) if schedule.remaining > 0 => {
236 schedule.remaining -= 1;
237 ctx.record("retry", "fail");
238 tokio::select! {
239 _ = ctx.cancel.cancelled() => return Err(Exit::Interrupt),
240 _ = tokio::time::sleep(schedule.delay) => {}
241 }
242 }
243 Err(error) => return Err(error),
244 }
245 }
246 })
247 })
248 }
249
250 pub fn timeout(&self, duration: Duration) -> Self {
251 let run = Arc::clone(&self.run);
252 Self::new(move |ctx| {
253 let run = Arc::clone(&run);
254 let child = ctx.child();
255 Box::pin(async move {
256 let child_cancel = child.cancel.clone();
257 let fut = run(child);
258 match tokio::time::timeout(duration, fut).await {
259 Ok(result) => result,
260 Err(_elapsed) => {
261 child_cancel.cancel();
262 Err(Exit::Interrupt)
263 }
264 }
265 })
266 })
267 }
268
269 pub fn with_span(&self, name: impl Into<String>) -> Self {
270 let name = name.into();
271 let run = Arc::clone(&self.run);
272 Self::new(move |ctx| {
273 let run = Arc::clone(&run);
274 let name = name.clone();
275 let fut = run(ctx.clone());
276 Box::pin(async move {
277 let result = fut.await;
278 let outcome = match &result {
279 Ok(_) => "ok",
280 Err(Exit::Fail(_)) => "fail",
281 Err(Exit::Die(_)) => "die",
282 Err(Exit::Interrupt) => "interrupt",
283 };
284 ctx.record(name, outcome);
285 result
286 })
287 })
288 }
289
290 pub fn zip_par<B>(&self, right: &Effect<B, E, S>) -> Effect<(A, B), E, S>
293 where
294 B: Send + Sync + 'static,
295 {
296 let left_run = Arc::clone(&self.run);
297 let right_run = Arc::clone(&right.run);
298 Effect::new(move |ctx| {
299 let left_run = Arc::clone(&left_run);
300 let right_run = Arc::clone(&right_run);
301 let left_ctx = ctx.child();
302 let right_ctx = ctx.child();
303 Box::pin(async move {
304 let left = left_run(left_ctx.clone());
305 let right = right_run(right_ctx.clone());
306 tokio::pin!(left, right);
307 tokio::select! {
308 left_result = &mut left => match left_result {
309 Ok(left_value) => match right.await {
310 Ok(right_value) => Ok((left_value, right_value)),
311 Err(error) => Err(error),
312 },
313 Err(error) => {
314 right_ctx.cancel.cancel();
315 let _ = right.await;
316 Err(error)
317 }
318 },
319 right_result = &mut right => match right_result {
320 Ok(right_value) => match left.await {
321 Ok(left_value) => Ok((left_value, right_value)),
322 Err(error) => Err(error),
323 },
324 Err(error) => {
325 left_ctx.cancel.cancel();
326 let _ = left.await;
327 Err(error)
328 }
329 },
330 }
331 })
332 })
333 }
334
335 pub fn race(&self, other: &Self) -> Self {
337 let left_run = Arc::clone(&self.run);
338 let right_run = Arc::clone(&other.run);
339 Self::new(move |ctx| {
340 let left_run = Arc::clone(&left_run);
341 let right_run = Arc::clone(&right_run);
342 let left_ctx = ctx.child();
343 let right_ctx = ctx.child();
344 Box::pin(async move {
345 let left = left_run(left_ctx.clone());
346 let right = right_run(right_ctx.clone());
347 tokio::pin!(left, right);
348 tokio::select! {
349 left_result = &mut left => {
350 right_ctx.cancel.cancel();
351 let _ = right.await;
352 left_result
353 }
354 right_result = &mut right => {
355 left_ctx.cancel.cancel();
356 let _ = left.await;
357 right_result
358 }
359 }
360 })
361 })
362 }
363
364 pub fn bracket<B, F, R>(&self, release: R, body: F) -> Effect<B, E, S>
367 where
368 A: Clone,
369 B: Send + Sync + 'static,
370 F: Fn(A) -> Effect<B, E, S> + Send + Sync + 'static,
371 R: Fn(A) + Send + Sync + 'static,
372 {
373 let acquire = Arc::clone(&self.run);
374 let release: Arc<dyn Fn(A) + Send + Sync> = Arc::new(release);
375 let body = Arc::new(body);
376 Effect::new(move |ctx| {
377 let acquire = Arc::clone(&acquire);
378 let release = Arc::clone(&release);
379 let body = Arc::clone(&body);
380 Box::pin(async move {
381 let resource = match acquire(ctx.clone()).await {
382 Ok(resource) => resource,
383 Err(error) => return Err(error),
384 };
385 let guard = ReleaseOnce::new(resource.clone(), Arc::clone(&release));
386 let result = body(resource).run_in(ctx).await;
387 guard.release_now();
388 result
389 })
390 })
391 }
392
393 pub fn map_services<S2>(
395 self,
396 map: impl Fn(Arc<S2>) -> Arc<S> + Send + Sync + 'static,
397 ) -> Effect<A, E, S2>
398 where
399 S2: Send + Sync + 'static,
400 {
401 let run = self.run;
402 Effect::new(move |ctx| {
403 let mapped = RunCtx {
404 services: map(ctx.services),
405 cancel: ctx.cancel,
406 trace: ctx.trace,
407 };
408 run(mapped)
409 })
410 }
411
412 pub fn provide<S2>(self, services: S) -> Effect<A, E, S2>
415 where
416 S2: Send + Sync + 'static,
417 {
418 let services = Arc::new(services);
419 let run = self.run;
420 Effect::new(move |ctx| {
421 let run = Arc::clone(&run);
422 let services = Arc::clone(&services);
423 let ctx = RunCtx {
424 services,
425 cancel: ctx.cancel,
426 trace: ctx.trace,
427 };
428 run(ctx)
429 })
430 }
431
432 pub async fn run(self, services: Arc<S>) -> (Result<A, Exit<E>>, Vec<TraceEvent>) {
433 self.run_with_cancel(services, CancellationToken::new())
434 .await
435 }
436
437 pub async fn run_with_cancel(
440 self,
441 services: Arc<S>,
442 cancel: CancellationToken,
443 ) -> (Result<A, Exit<E>>, Vec<TraceEvent>) {
444 let trace = Arc::new(Mutex::new(Vec::new()));
445 let ctx = RunCtx {
446 services,
447 cancel,
448 trace: Arc::clone(&trace),
449 };
450 let result = self.run_in(ctx).await;
451 let events = trace
452 .lock()
453 .map(|events| events.clone())
454 .unwrap_or_default();
455 (result, events)
456 }
457
458 pub(crate) async fn run_in(&self, ctx: RunCtx<S>) -> Result<A, Exit<E>> {
459 (self.run)(ctx).await
460 }
461}
462
463#[derive(Debug, Clone, Copy)]
464pub struct Schedule {
465 pub remaining: u32,
466 pub delay: Duration,
467}
468
469struct ReleaseOnce<T> {
470 value: Mutex<Option<T>>,
471 release: Arc<dyn Fn(T) + Send + Sync>,
472}
473
474impl<T> ReleaseOnce<T> {
475 fn new(value: T, release: Arc<dyn Fn(T) + Send + Sync>) -> Self {
476 Self {
477 value: Mutex::new(Some(value)),
478 release,
479 }
480 }
481
482 fn release_now(&self) {
483 if let Ok(mut slot) = self.value.lock() {
484 if let Some(value) = slot.take() {
485 (self.release)(value);
486 }
487 }
488 }
489}
490
491impl<T> Drop for ReleaseOnce<T> {
492 fn drop(&mut self) {
493 self.release_now();
494 }
495}