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 // `legacy()` is little-endian + fixint, byte-identical to bincode 1.x's
67 // default, so queues written before this upgrade still decode.
68 bincode::serde::encode_to_vec(value, bincode::config::legacy()).map_err(CodecError::new)
69 }
70
71 fn decode(&self, bytes: &[u8]) -> Result<T, CodecError> {
72 bincode::serde::decode_from_slice(bytes, bincode::config::legacy())
73 .map(|(value, _)| value)
74 .map_err(CodecError::new)
75 }
76}
77
78/// A [`Codec`] that encodes with rkyv. Requires the `rkyv` feature. The message type
79/// must derive rkyv's `Archive`, `Serialize`, and `Deserialize`.
80///
81/// ```
82/// use persistent_queue::{Builder, MemStore, Rkyv};
83///
84/// #[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, PartialEq)]
85/// struct Job {
86/// id: u64,
87/// name: String,
88/// }
89///
90/// let (tx, rx) = Builder::new(MemStore::new()).open_typed(Rkyv).unwrap();
91/// tx.push(&Job { id: 1, name: "build".into() }).unwrap();
92///
93/// let item = rx.reserve().unwrap().unwrap();
94/// assert_eq!(*item, Job { id: 1, name: "build".into() });
95/// item.ack().unwrap();
96/// ```
97#[cfg(feature = "rkyv")]
98#[derive(Clone, Copy, Debug, Default)]
99pub struct Rkyv;
100
101#[cfg(feature = "rkyv")]
102impl<T> Codec<T> for Rkyv
103where
104 T: rkyv::Archive
105 + for<'a> rkyv::Serialize<
106 rkyv::rancor::Strategy<
107 rkyv::ser::Serializer<
108 rkyv::util::AlignedVec,
109 rkyv::ser::allocator::ArenaHandle<'a>,
110 rkyv::ser::sharing::Share,
111 >,
112 rkyv::rancor::Error,
113 >,
114 >,
115 T::Archived: for<'a> rkyv::bytecheck::CheckBytes<rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>>
116 + rkyv::Deserialize<T, rkyv::api::high::HighDeserializer<rkyv::rancor::Error>>,
117{
118 fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError> {
119 rkyv::to_bytes::<rkyv::rancor::Error>(value)
120 .map(|bytes| bytes.to_vec())
121 .map_err(CodecError::new)
122 }
123
124 fn decode(&self, bytes: &[u8]) -> Result<T, CodecError> {
125 // rkyv needs an aligned buffer; the store hands back a plain `Vec<u8>`.
126 let mut aligned = rkyv::util::AlignedVec::<16>::new();
127 aligned.extend_from_slice(bytes);
128 rkyv::from_bytes::<T, rkyv::rancor::Error>(&aligned).map_err(CodecError::new)
129 }
130}