paladin/task/
mod.rs

1//! Task and TaskResult types.
2//!
3//! Tasks encode an [`Operation`] paired with arguments and additional metadata.
4//! They represent the payloads used to communicate between
5//! [`Runtime`](crate::runtime::Runtime)s and
6//! [`WorkerRuntime`](crate::runtime::WorkerRuntime)s.
7use std::fmt::Debug;
8
9use anyhow::Result;
10use bytes::Bytes;
11use serde::{Deserialize, Serialize};
12
13use crate::{
14    __private::OPERATIONS,
15    operation::Operation,
16    serializer::{Serializable, Serializer},
17};
18
19/// A [`Task`] encodes an [`Operation`] paired with arguments.
20///
21/// In addition to the [`Operation`] and its arguments, a [`Task`] also contains
22/// metadata and routing information. The routing information is used to
23/// identify the [`Channel`](crate::channel::Channel) to which execution results
24/// should be sent.
25///
26/// Metadata can be any arbitrary [`Serializable`] type.
27/// It's typically used by [`Directive`](crate::directive::Directive)s to encode
28/// additional information about the computation.
29#[derive(Debug)]
30pub struct Task<'a, Op: Operation, Metadata: Serializable> {
31    /// The routing key used to identify the
32    /// [`Channel`](crate::channel::Channel) to which execution results should
33    /// be sent.
34    pub routing_key: String,
35    /// Metadata associated with the [`Task`].
36    pub metadata: Metadata,
37    /// The [`Operation`] to be executed.
38    pub op: &'a Op,
39    /// The arguments to the [`Operation`].
40    pub input: Op::Input,
41}
42
43/// A [`TaskResult`] encodes the result of executing a [`Task`].
44///
45/// The [`TaskResult`] passes back whatever metadata was associated with the
46/// [`Task`] that produced it.
47#[derive(Serialize, Deserialize, Debug)]
48#[serde(bound = "Op: Operation")]
49pub struct TaskOutput<Op: Operation, Metadata: Serializable> {
50    /// Metadata associated with the [`Task`] that produced this result.
51    pub metadata: Metadata,
52    /// The output of the [`Operation`] execution.
53    pub output: Op::Output,
54}
55
56pub type TaskResult<Op, Metadata> = Result<TaskOutput<Op, Metadata>>;
57
58/// A [`Task`] that has been serialized for remote execution.
59///
60/// This type is used to facilitate opaque execution of [`Operation`]s, such
61/// that executors can execute arbitrary [`Operation`]s.
62#[derive(Serialize, Deserialize, Debug, Clone)]
63pub struct AnyTask {
64    /// The routing key used to identify the
65    /// [`Channel`](crate::channel::Channel) to which execution results should
66    /// be sent.
67    pub routing_key: String,
68    /// Serialized metadata associated with the [`Task`].
69    pub metadata: Bytes,
70    /// The serialized [`Operation`] to be executed.
71    pub op: Bytes,
72    /// The unique identifier of the [`Operation`] to be executed.
73    pub operation_id: u8,
74    /// Serialized arguments to the [`Operation`].
75    pub input: Bytes,
76    /// The [`Serializer`] used to serialize and deserialize the [`Operation`]
77    /// arguments.
78    pub serializer: Serializer,
79}
80
81/// Serialized output of a [`Task`].
82#[derive(Serialize, Deserialize, Debug)]
83pub struct AnyTaskOutput {
84    /// Serialized metadata associated with the [`Task`].
85    pub metadata: Bytes,
86    /// Serialized output of the [`Operation`] execution.
87    pub output: Bytes,
88    /// The [`Serializer`] used to serialize and deserialize the [`Operation`].
89    pub serializer: Serializer,
90}
91
92impl<Op: Operation, Metadata: Serializable> TryFrom<AnyTaskOutput> for TaskOutput<Op, Metadata> {
93    type Error = anyhow::Error;
94
95    fn try_from(
96        AnyTaskOutput {
97            metadata,
98            output,
99            serializer,
100        }: AnyTaskOutput,
101    ) -> Result<Self> {
102        let metadata = serializer.from_bytes(&metadata)?;
103        let output = serializer.from_bytes(&output)?;
104
105        Ok(TaskOutput { metadata, output })
106    }
107}
108
109/// A serializable `Result` type for [`AnyTaskOutput`].
110///
111/// `Result` isn't serializable, so we need to wrap it in a type that is.
112#[derive(Serialize, Deserialize, Debug)]
113pub enum AnyTaskResult {
114    Ok(AnyTaskOutput),
115    Err(String),
116}
117
118impl<'a, Op: Operation, Metadata: Serializable> Task<'a, Op, Metadata> {
119    /// Convert a [`Task`] into an opaque [`AnyTask`].
120    pub fn as_any_task(&self, serializer: Serializer) -> Result<AnyTask> {
121        let routing_key = self.routing_key.clone();
122        let metadata = serializer.to_bytes(&self.metadata)?;
123        let input = serializer.to_bytes(&self.input)?;
124        let op = serializer.to_bytes(self.op)?;
125
126        Ok(AnyTask {
127            routing_key,
128            metadata,
129            operation_id: Op::ID,
130            op,
131            input,
132            serializer,
133        })
134    }
135}
136
137impl AnyTaskResult {
138    /// Convert an opaque [`AnyTaskResult`] into a typed [`TaskResult`].
139    pub fn into_task_result<Op: Operation, Metadata: Serializable>(
140        self,
141    ) -> TaskResult<Op, Metadata> {
142        match self {
143            Self::Ok(any_task_output) => Ok(any_task_output.try_into()?),
144            Self::Err(msg) => Err(anyhow::anyhow!(msg)),
145        }
146    }
147}
148
149impl<Op: Operation, Metadata: Serializable> From<AnyTaskResult>
150    for Result<TaskOutput<Op, Metadata>>
151{
152    fn from(value: AnyTaskResult) -> Self {
153        value.into_task_result()
154    }
155}
156
157impl AnyTask {
158    /// Opaque execution of a [`Task`].
159    ///
160    /// This function is used to execute arbitrary [`Operation`]s. It uses the
161    /// [`RemoteExecute::ID`](crate::operation::RemoteExecute::ID) field to
162    /// acquire the correct execution pointer from the [`static@OPERATIONS`]
163    /// slice.
164    pub async fn remote_execute(self) -> crate::operation::Result<AnyTaskOutput> {
165        OPERATIONS[self.operation_id as usize](self).await
166    }
167}