Skip to main content

persistent_queue/
codec.rs

1//! The [`Codec`] trait and built-in codecs for the typed queue layer.
2
3use std::error::Error as StdError;
4use std::fmt;
5
6/// Encodes a message type to bytes for the store and decodes it back.
7///
8/// The typed layer ([`TypedProducer`](crate::TypedProducer) /
9/// [`TypedConsumer`](crate::TypedConsumer)) is generic over this trait: implement it
10/// for a custom format, or use a built-in like [`Bincode`] behind the `serde` feature.
11pub trait Codec<T> {
12    /// Encode `value` to bytes.
13    fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError>;
14    /// Decode a value from `bytes`.
15    fn decode(&self, bytes: &[u8]) -> Result<T, CodecError>;
16}
17
18/// An encode or decode failure, carrying the underlying codec's message.
19#[derive(Debug)]
20pub struct CodecError(String);
21
22impl CodecError {
23    /// Build a codec error from anything printable, e.g. the codec's own error.
24    pub fn new(error: impl fmt::Display) -> Self {
25        Self(error.to_string())
26    }
27}
28
29impl fmt::Display for CodecError {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "codec error: {}", self.0)
32    }
33}
34
35impl StdError for CodecError {}
36
37/// A [`Codec`] that encodes with serde and bincode. Requires the `serde` feature.
38///
39/// ```
40/// use persistent_queue::{Bincode, Builder, MemStore};
41/// use serde::{Deserialize, Serialize};
42///
43/// #[derive(Serialize, Deserialize, Debug, PartialEq)]
44/// struct Job {
45///     id: u64,
46///     name: String,
47/// }
48///
49/// let (tx, rx) = Builder::new(MemStore::new()).open_typed(Bincode).unwrap();
50/// tx.push(&Job { id: 1, name: "build".into() }).unwrap();
51///
52/// let item = rx.reserve().unwrap().unwrap();
53/// assert_eq!(*item, Job { id: 1, name: "build".into() });
54/// item.ack().unwrap();
55/// ```
56#[cfg(feature = "serde")]
57#[derive(Clone, Copy, Debug, Default)]
58pub struct Bincode;
59
60#[cfg(feature = "serde")]
61impl<T> Codec<T> for Bincode
62where
63    T: serde::Serialize + serde::de::DeserializeOwned,
64{
65    fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError> {
66        bincode::serialize(value).map_err(CodecError::new)
67    }
68
69    fn decode(&self, bytes: &[u8]) -> Result<T, CodecError> {
70        bincode::deserialize(bytes).map_err(CodecError::new)
71    }
72}
73
74/// A [`Codec`] that encodes with rkyv. Requires the `rkyv` feature. The message type
75/// must derive rkyv's `Archive`, `Serialize`, and `Deserialize`.
76///
77/// ```
78/// use persistent_queue::{Builder, MemStore, Rkyv};
79///
80/// #[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, PartialEq)]
81/// struct Job {
82///     id: u64,
83///     name: String,
84/// }
85///
86/// let (tx, rx) = Builder::new(MemStore::new()).open_typed(Rkyv).unwrap();
87/// tx.push(&Job { id: 1, name: "build".into() }).unwrap();
88///
89/// let item = rx.reserve().unwrap().unwrap();
90/// assert_eq!(*item, Job { id: 1, name: "build".into() });
91/// item.ack().unwrap();
92/// ```
93#[cfg(feature = "rkyv")]
94#[derive(Clone, Copy, Debug, Default)]
95pub struct Rkyv;
96
97#[cfg(feature = "rkyv")]
98impl<T> Codec<T> for Rkyv
99where
100    T: rkyv::Archive
101        + for<'a> rkyv::Serialize<
102            rkyv::rancor::Strategy<
103                rkyv::ser::Serializer<
104                    rkyv::util::AlignedVec,
105                    rkyv::ser::allocator::ArenaHandle<'a>,
106                    rkyv::ser::sharing::Share,
107                >,
108                rkyv::rancor::Error,
109            >,
110        >,
111    T::Archived: for<'a> rkyv::bytecheck::CheckBytes<rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>>
112        + rkyv::Deserialize<T, rkyv::api::high::HighDeserializer<rkyv::rancor::Error>>,
113{
114    fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError> {
115        rkyv::to_bytes::<rkyv::rancor::Error>(value)
116            .map(|bytes| bytes.to_vec())
117            .map_err(CodecError::new)
118    }
119
120    fn decode(&self, bytes: &[u8]) -> Result<T, CodecError> {
121        // rkyv needs an aligned buffer; the store hands back a plain `Vec<u8>`.
122        let mut aligned = rkyv::util::AlignedVec::<16>::new();
123        aligned.extend_from_slice(bytes);
124        rkyv::from_bytes::<T, rkyv::rancor::Error>(&aligned).map_err(CodecError::new)
125    }
126}