Skip to main content

swiftide_tasks/
traits.rs

1//! Trait contracts used by task graphs.
2
3use std::{any::Any, sync::Arc};
4
5use async_trait::async_trait;
6use dyn_clone::DynClone;
7
8use super::{
9    errors::TaskError,
10    executor::EvaluatedTransition,
11    node::NodeId,
12    task::Task,
13    transition::{MarkedTransition, Transition},
14};
15
16/// A value that can flow into or out of a [`TaskNode`].
17///
18/// Task inputs, outputs, transition payloads, and join payloads all use this bound so they can be
19/// moved safely across async task execution.
20pub trait NodeArg: Send + Sync + DynClone + 'static {}
21
22impl<T: Send + Sync + std::fmt::Debug + 'static + Clone> NodeArg for T {}
23
24/// A typed step in a [`Task`](crate::Task).
25///
26/// Implement this trait for your own domain-specific nodes when you want full control over how a
27/// task step runs. For lightweight closure nodes, use
28/// [`Task::register_node_fn`](crate::Task::register_node_fn), or
29/// [`Task::register_node_async_fn`](crate::Task::register_node_async_fn) for async closures.
30#[async_trait]
31pub trait TaskNode: Send + Sync + DynClone + Any {
32    /// The input accepted by this node.
33    type Input: NodeArg;
34    /// The output produced by this node.
35    type Output: NodeArg;
36    /// The error returned when evaluation fails.
37    type Error: std::error::Error + Send + Sync + 'static;
38
39    /// Evaluates the node with the current input.
40    async fn evaluate(
41        &self,
42        node_id: &DynNodeId<Self>,
43        input: &Self::Input,
44    ) -> Result<Self::Output, Self::Error>;
45}
46
47/// Type-erased [`NodeId`] for the same input, output, and error types as `T`.
48pub type DynNodeId<T> = NodeId<
49    dyn TaskNode<
50            Input = <T as TaskNode>::Input,
51            Output = <T as TaskNode>::Output,
52            Error = <T as TaskNode>::Error,
53        >,
54>;
55
56dyn_clone::clone_trait_object!(
57    TaskNode<
58        Input = dyn NodeArg,
59        Output = dyn NodeArg,
60        Error = dyn std::error::Error + Send + Sync,
61    >
62);
63
64#[async_trait]
65impl<Input: NodeArg, Output: NodeArg, Error: std::error::Error + Send + Sync + 'static> TaskNode
66    for Box<dyn TaskNode<Input = Input, Output = Output, Error = Error>>
67{
68    type Input = Input;
69    type Output = Output;
70    type Error = Error;
71
72    async fn evaluate(
73        &self,
74        node_id: &NodeId<
75            dyn TaskNode<Input = Self::Input, Output = Self::Output, Error = Self::Error>,
76        >,
77        input: &Self::Input,
78    ) -> Result<Self::Output, Self::Error> {
79        self.as_ref().evaluate(node_id, input).await
80    }
81}
82
83dyn_clone::clone_trait_object!(<Input, Output, Error> TaskNode<Input = Input, Output = Output, Error = Error>);
84
85#[doc(hidden)]
86pub trait RegisterTransition<From: TaskNode + ?Sized>: 'static {
87    #[doc(hidden)]
88    fn register<Input: NodeArg + Clone, Output: NodeArg + Clone>(
89        self,
90        task: &mut Task<Input, Output>,
91        from: NodeId<From>,
92    ) -> Result<(), TaskError>;
93}
94
95#[doc(hidden)]
96pub trait RegisterTransitionAsync<From: TaskNode + ?Sized>: 'static {
97    #[doc(hidden)]
98    fn register_async<Input: NodeArg + Clone, Output: NodeArg + Clone>(
99        self,
100        task: &mut Task<Input, Output>,
101        from: NodeId<From>,
102    ) -> Result<(), TaskError>;
103}
104
105pub(super) trait TransitionResult<From: TaskNode + ?Sized> {
106    fn into_transition(self) -> Transition;
107}
108
109impl<From, To> TransitionResult<From> for MarkedTransition<To>
110where
111    From: TaskNode + 'static + ?Sized,
112    To: TaskNode + ?Sized,
113{
114    fn into_transition(self) -> Transition {
115        self.into_inner()
116    }
117}
118
119impl<From> TransitionResult<From> for Transition
120where
121    From: TaskNode + 'static + ?Sized,
122{
123    fn into_transition(self) -> Transition {
124        self
125    }
126}
127
128/// Type-erased node executor used by the runtime.
129#[async_trait]
130pub(crate) trait AnyNodeExecutor: Any + Send + Sync + std::fmt::Debug + DynClone {
131    fn node_as_any(&self) -> &dyn Any;
132
133    fn transition_is_set(&self) -> bool;
134
135    async fn evaluate_next(
136        &self,
137        context: Arc<dyn Any + Send + Sync>,
138    ) -> Result<EvaluatedTransition, TaskError>;
139}
140
141dyn_clone::clone_trait_object!(AnyNodeExecutor);