Common/Effect/
ActionEffect.rs

1//! # ActionEffect Struct
2//!
3//! Defines the core `ActionEffect` struct, which is the fundamental unit of
4//! computation in the application's declarative, effects-based architecture.
5
6use std::{future::Future, pin::Pin, sync::Arc};
7
8/// An `ActionEffect` encapsulates an asynchronous operation as a first-class
9/// value.
10///
11/// It is a data structure that contains a function. This function, when
12/// provided with its required capability (`TCapability`), produces a `Future`
13/// that will yield the result of the operation. This pattern cleanly separates
14/// the *definition* of an operation from its *execution*.
15///
16/// # Type Parameters
17///
18/// * `TCapability`: The type of the capability (e.g., `Arc<dyn
19///   FileSystemReader>`) that the effect's closure requires to run.
20/// * `TError`: The error type that the effect's operation can return.
21/// * `TOutput`: The success output type of the effect's operation.
22pub struct ActionEffect<TCapability, TError, TOutput> {
23	/// The wrapped asynchronous function. It is stored in an `Arc` to make the
24	/// `ActionEffect` struct itself cheap to clone.
25	pub Function:
26		Arc<dyn Fn(TCapability) -> Pin<Box<dyn Future<Output = Result<TOutput, TError>> + Send>> + Send + Sync>,
27}
28
29impl<TCapability, TError, TOutput> ActionEffect<TCapability, TError, TOutput> {
30	/// Creates a new `ActionEffect` from a given function closure.
31	pub fn New(
32		Function:Arc<
33			dyn Fn(TCapability) -> Pin<Box<dyn Future<Output = Result<TOutput, TError>> + Send>> + Send + Sync,
34		>,
35	) -> Self {
36		Self { Function }
37	}
38
39	/// Applies the effect by executing its wrapped function with the provided
40	/// capability. This is typically called by an `ApplicationRunTime`.
41	pub async fn Apply(&self, Capability:TCapability) -> Result<TOutput, TError>
42	where
43		TCapability: Clone, {
44		(self.Function)(Capability).await
45	}
46
47	/// Transforms the output of an effect from `TOutput` to `TNewOutput`.
48	pub fn map<TNewOutput, F>(self, _Function:F) -> ActionEffect<TCapability, TError, TNewOutput>
49	where
50		TCapability: 'static + Send,
51		TError: 'static,
52		TOutput: 'static + Send,
53		TNewOutput: 'static,
54		F: Fn(TOutput) -> TNewOutput + Send + Sync + 'static + Copy, {
55		ActionEffect::New(Arc::new(move |Capability| {
56			let Function = self.Function.clone();
57
58			Box::pin(async move {
59				let Result = (Function)(Capability).await?;
60
61				Ok(_Function(Result))
62			})
63		}))
64	}
65}
66
67impl<TCapability, TError, TOutput> Clone for ActionEffect<TCapability, TError, TOutput> {
68	/// Clones the `ActionEffect`. This is a cheap operation as it only clones
69	/// the `Arc` pointer to the underlying function.
70	fn clone(&self) -> Self { ActionEffect { Function:Arc::clone(&self.Function) } }
71}