1use crate::content::ContentType;
2use crate::error::DecodeError;
3use crate::kv::KvEntry;
4use crate::query::Row;
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7
8pub trait Codec<T: ?Sized> {
34 fn content_type() -> ContentType;
36 fn encode(value: &T) -> Result<Vec<u8>, DecodeError>;
38}
39
40pub trait Decoder<T> {
57 fn decode(payload: &[u8]) -> Result<T, DecodeError>;
59}
60
61#[derive(Clone, Copy, Debug, Default)]
63pub struct Json;
64
65impl<T: Serialize + ?Sized> Codec<T> for Json {
66 fn content_type() -> ContentType {
67 ContentType::Json
68 }
69 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
70 serde_json::to_vec(value)
71 .map_err(|error| DecodeError::Encode(format!("encode JSON payload: {error}")))
72 }
73}
74
75impl<T: DeserializeOwned> Decoder<T> for Json {
76 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
77 serde_json::from_slice(payload)
78 .map_err(|error| DecodeError::Decode(format!("decode JSON payload: {error}")))
79 }
80}
81
82#[derive(Clone, Copy, Debug, Default)]
86pub struct Msgpack;
87
88impl<T: Serialize + ?Sized> Codec<T> for Msgpack {
89 fn content_type() -> ContentType {
90 ContentType::Msgpack
91 }
92 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
93 rmp_serde::to_vec_named(value)
94 .map_err(|error| DecodeError::Encode(format!("encode msgpack payload: {error}")))
95 }
96}
97
98impl<T: DeserializeOwned> Decoder<T> for Msgpack {
99 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
100 rmp_serde::from_slice(payload)
101 .map_err(|error| DecodeError::Decode(format!("decode msgpack payload: {error}")))
102 }
103}
104
105#[derive(Clone, Copy, Debug, Default)]
109pub struct Cbor;
110
111impl<T: Serialize + ?Sized> Codec<T> for Cbor {
112 fn content_type() -> ContentType {
113 ContentType::Cbor
114 }
115 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
116 let mut buffer = Vec::new();
117 ciborium::into_writer(value, &mut buffer)
118 .map_err(|error| DecodeError::Encode(format!("encode CBOR payload: {error}")))?;
119 Ok(buffer)
120 }
121}
122
123impl<T: DeserializeOwned> Decoder<T> for Cbor {
124 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
125 ciborium::from_reader(payload)
126 .map_err(|error| DecodeError::Decode(format!("decode CBOR payload: {error}")))
127 }
128}
129
130#[cfg(feature = "bson")]
134#[derive(Clone, Copy, Debug, Default)]
135pub struct Bson;
136
137#[cfg(feature = "bson")]
138impl<T: Serialize> Codec<T> for Bson {
139 fn content_type() -> ContentType {
140 ContentType::Bson
141 }
142 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
143 bson::serialize_to_vec(value)
144 .map_err(|error| DecodeError::Encode(format!("encode BSON payload: {error}")))
145 }
146}
147
148#[cfg(feature = "bson")]
149impl<T: DeserializeOwned> Decoder<T> for Bson {
150 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
151 bson::deserialize_from_slice(payload)
152 .map_err(|error| DecodeError::Decode(format!("decode BSON payload: {error}")))
153 }
154}
155
156const NO_ROW_PAYLOAD: &str = "row has no payload, the publisher must call .inline_payload() and the query must call .with_payload()";
157
158impl Row {
159 pub fn decode_json<T: DeserializeOwned>(&self) -> Result<T, DecodeError> {
163 self.decode_with::<Json, T>()
164 }
165
166 pub fn decode_msgpack<T: DeserializeOwned>(&self) -> Result<T, DecodeError> {
169 self.decode_with::<Msgpack, T>()
170 }
171
172 pub fn decode_with<C, T>(&self) -> Result<T, DecodeError>
177 where
178 C: Decoder<T>,
179 {
180 let payload = self
181 .payload
182 .as_deref()
183 .ok_or(DecodeError::MissingPayload(NO_ROW_PAYLOAD))?;
184 C::decode(payload)
185 }
186}
187
188impl KvEntry {
189 pub fn decode_value<T: DeserializeOwned>(&self) -> Result<T, DecodeError> {
191 self.decode_value_with::<Json, T>()
192 }
193
194 pub fn decode_value_with<C, T>(&self) -> Result<T, DecodeError>
196 where
197 C: Decoder<T>,
198 {
199 C::decode(&self.value)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
208 struct Body {
209 id: u32,
210 name: String,
211 }
212
213 fn body() -> Body {
214 Body {
215 id: 7,
216 name: "alice".to_owned(),
217 }
218 }
219
220 #[test]
221 fn given_a_row_payload_when_decoded_with_each_codec_then_should_round_trip() {
222 let json_row = Row {
223 payload: Some(Json::encode(&body()).expect("json encode")),
224 ..Default::default()
225 };
226 assert_eq!(json_row.decode_with::<Json, Body>().expect("json"), body());
227 let msgpack_row = Row {
228 payload: Some(Msgpack::encode(&body()).expect("msgpack encode")),
229 ..Default::default()
230 };
231 assert_eq!(
232 msgpack_row.decode_with::<Msgpack, Body>().expect("msgpack"),
233 body()
234 );
235 let cbor_row = Row {
236 payload: Some(Cbor::encode(&body()).expect("cbor encode")),
237 ..Default::default()
238 };
239 assert_eq!(cbor_row.decode_with::<Cbor, Body>().expect("cbor"), body());
240 }
241
242 #[cfg(feature = "bson")]
243 #[test]
244 fn given_a_bson_payload_when_decoded_then_should_round_trip() {
245 let bson_row = Row {
246 payload: Some(Bson::encode(&body()).expect("bson encode")),
247 ..Default::default()
248 };
249 assert_eq!(bson_row.decode_with::<Bson, Body>().expect("bson"), body());
250 assert_eq!(<Bson as Codec<Body>>::content_type(), ContentType::Bson);
251 }
252
253 #[test]
254 fn given_each_codec_when_encoding_then_should_advertise_its_content_type() {
255 assert_eq!(<Json as Codec<str>>::content_type(), ContentType::Json);
256 assert_eq!(
257 <Msgpack as Codec<str>>::content_type(),
258 ContentType::Msgpack
259 );
260 assert_eq!(<Cbor as Codec<str>>::content_type(), ContentType::Cbor);
261 }
262
263 #[test]
264 fn given_a_row_without_payload_when_decoded_then_should_error() {
265 let row = Row::default();
266 assert!(matches!(
267 row.decode_with::<Json, String>(),
268 Err(DecodeError::MissingPayload(_))
269 ));
270 }
271
272 #[test]
273 fn given_a_msgpack_value_when_round_tripped_through_the_entry_then_should_decode_back() {
274 let encoded = Msgpack::encode(&vec!["a", "b"]).expect("encode");
275 let entry = KvEntry {
276 key: b"k".to_vec(),
277 value: encoded,
278 expires_at_micros: None,
279 version: 0,
280 scope: None,
281 source: None,
282 };
283 let decoded: Vec<String> = entry.decode_value_with::<Msgpack, _>().expect("decode");
284 assert_eq!(decoded, vec!["a".to_owned(), "b".to_owned()]);
285 }
286}