apalis_core/backend/codec.rs
1//! Utilities for encoding and decoding task arguments and results
2//!
3//! # Overview
4//!
5//! The `Codec` trait allows for converting values
6//! between a type `T` and a more compact or transport-friendly representation.
7//! This is particularly useful for serializing/deserializing, compressing/expanding,
8//! or otherwise encoding/decoding values in a custom format.
9//!
10//! The module includes several implementations of the `Codec` trait, such as `IdentityCodec`
11//! and `NoopCodec`, as well as a JSON codec when the `json` feature is enabled.
12
13use crate::{
14 backend::{Backend, BackendExt},
15 worker::context::WorkerContext,
16};
17/// A trait for converting values between a type `T` and a more compact or
18/// transport-friendly representation for a `Backend`. Examples include json
19/// and bytes.
20///
21/// This is useful when you need to serialize/deserialize, compress/expand,
22/// or otherwise encode/decode values in a custom format.
23///
24/// By default, a backend doesn't care about the specific type implementing [`Codec`]
25/// but rather the [`Codec::Compact`] type. This means if it can accept bytes, you
26/// can use familiar crates such as bincode and rkyv
27///
28/// # Type Parameters
29/// - `T`: The type of value being encoded/decoded.
30pub trait Codec<T> {
31 /// The error type returned if encoding or decoding fails.
32 type Error;
33
34 /// The compact or encoded representation of `T`.
35 ///
36 /// This could be a primitive type, a byte buffer, or any other
37 /// representation that is more efficient to store or transmit.
38 type Compact;
39
40 /// Encode a value of type `T` into its compact representation.
41 ///
42 /// # Errors
43 /// Returns [`Self::Error`] if the value cannot be encoded.
44 fn encode(val: &T) -> Result<Self::Compact, Self::Error>;
45
46 /// Decode a compact representation back into a value of type `T`.
47 ///
48 /// # Errors
49 /// Returns [`Self::Error`] if the compact representation cannot
50 /// be decoded into a valid `T`.
51 fn decode(val: &Self::Compact) -> Result<T, Self::Error>;
52}
53
54/// A codec that performs no transformation, returning the input value as-is.
55#[derive(Debug, Clone, Default)]
56pub struct IdentityCodec;
57
58impl<T> Codec<T> for IdentityCodec
59where
60 T: Clone,
61{
62 type Compact = T;
63 type Error = std::convert::Infallible;
64
65 fn encode(val: &T) -> Result<Self::Compact, Self::Error> {
66 Ok(val.clone())
67 }
68
69 fn decode(val: &Self::Compact) -> Result<T, Self::Error> {
70 Ok(val.clone())
71 }
72}
73
74/// Wrapper that skips decoding and works directly with compact representation.
75///
76/// This is useful for backends that natively handle compact types and do not know the types at compile time.
77/// Examples include backends that work with raw bytes or JSON values like workflows that manipulate dynamic data.
78#[derive(Debug, Clone)]
79pub struct RawDataBackend<B> {
80 inner: B,
81}
82
83impl<B> RawDataBackend<B> {
84 /// Create a new `RawDataBackend` wrapping the given backend.
85 pub fn new(backend: B) -> Self {
86 Self { inner: backend }
87 }
88}
89
90impl<B> Backend for RawDataBackend<B>
91where
92 B: BackendExt,
93{
94 type Args = B::Compact;
95 type IdType = B::IdType;
96 type Context = B::Context;
97 type Error = B::Error;
98 type Stream = B::CompactStream;
99 type Beat = B::Beat;
100 type Layer = B::Layer;
101
102 fn heartbeat(&self, worker: &WorkerContext) -> Self::Beat {
103 self.inner.heartbeat(worker)
104 }
105
106 fn middleware(&self) -> Self::Layer {
107 self.inner.middleware()
108 }
109
110 fn poll(self, worker: &WorkerContext) -> Self::Stream {
111 self.inner.poll_compact(worker)
112 }
113}