1use crate::content::ContentType;
2use crate::error::DecodeError;
3use crate::kv::KvEntry;
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6
7pub trait Codec<T: ?Sized> {
33 fn content_type() -> ContentType;
35 fn encode(value: &T) -> Result<Vec<u8>, DecodeError>;
37}
38
39pub trait Decoder<T> {
56 fn decode(payload: &[u8]) -> Result<T, DecodeError>;
58}
59
60#[derive(Clone, Copy, Debug, Default)]
62pub struct Json;
63
64impl<T: Serialize + ?Sized> Codec<T> for Json {
65 fn content_type() -> ContentType {
66 ContentType::Json
67 }
68 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
69 serde_json::to_vec(value)
70 .map_err(|error| DecodeError::Encode(format!("encode JSON payload: {error}")))
71 }
72}
73
74impl<T: DeserializeOwned> Decoder<T> for Json {
75 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
76 serde_json::from_slice(payload)
77 .map_err(|error| DecodeError::Decode(format!("decode JSON payload: {error}")))
78 }
79}
80
81#[derive(Clone, Copy, Debug, Default)]
85pub struct Msgpack;
86
87impl<T: Serialize + ?Sized> Codec<T> for Msgpack {
88 fn content_type() -> ContentType {
89 ContentType::Msgpack
90 }
91 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
92 rmp_serde::to_vec_named(value)
93 .map_err(|error| DecodeError::Encode(format!("encode msgpack payload: {error}")))
94 }
95}
96
97impl<T: DeserializeOwned> Decoder<T> for Msgpack {
98 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
99 rmp_serde::from_slice(payload)
100 .map_err(|error| DecodeError::Decode(format!("decode msgpack payload: {error}")))
101 }
102}
103
104#[derive(Clone, Copy, Debug, Default)]
108pub struct Cbor;
109
110impl<T: Serialize + ?Sized> Codec<T> for Cbor {
111 fn content_type() -> ContentType {
112 ContentType::Cbor
113 }
114 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
115 let mut buffer = Vec::new();
116 ciborium::into_writer(value, &mut buffer)
117 .map_err(|error| DecodeError::Encode(format!("encode CBOR payload: {error}")))?;
118 Ok(buffer)
119 }
120}
121
122impl<T: DeserializeOwned> Decoder<T> for Cbor {
123 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
124 ciborium::from_reader(payload)
125 .map_err(|error| DecodeError::Decode(format!("decode CBOR payload: {error}")))
126 }
127}
128
129#[cfg(feature = "bson")]
133#[derive(Clone, Copy, Debug, Default)]
134pub struct Bson;
135
136#[cfg(feature = "bson")]
137impl<T: Serialize> Codec<T> for Bson {
138 fn content_type() -> ContentType {
139 ContentType::Bson
140 }
141 fn encode(value: &T) -> Result<Vec<u8>, DecodeError> {
142 bson::serialize_to_vec(value)
143 .map_err(|error| DecodeError::Encode(format!("encode BSON payload: {error}")))
144 }
145}
146
147#[cfg(feature = "bson")]
148impl<T: DeserializeOwned> Decoder<T> for Bson {
149 fn decode(payload: &[u8]) -> Result<T, DecodeError> {
150 bson::deserialize_from_slice(payload)
151 .map_err(|error| DecodeError::Decode(format!("decode BSON payload: {error}")))
152 }
153}
154
155impl KvEntry {
156 pub fn decode_value<T: DeserializeOwned>(&self) -> Result<T, DecodeError> {
158 self.decode_value_with::<Json, T>()
159 }
160
161 pub fn decode_value_with<C, T>(&self) -> Result<T, DecodeError>
163 where
164 C: Decoder<T>,
165 {
166 C::decode(&self.value)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
175 struct Body {
176 id: u32,
177 name: String,
178 }
179
180 fn body() -> Body {
181 Body {
182 id: 7,
183 name: "alice".to_owned(),
184 }
185 }
186
187 #[test]
188 fn given_a_body_when_encoded_with_each_codec_then_should_round_trip() {
189 let json = Json::encode(&body()).expect("json encode");
190 let decoded: Body = Json::decode(&json).expect("json");
191 assert_eq!(decoded, body());
192 let msgpack = Msgpack::encode(&body()).expect("msgpack encode");
193 let decoded: Body = Msgpack::decode(&msgpack).expect("msgpack");
194 assert_eq!(decoded, body());
195 let cbor = Cbor::encode(&body()).expect("cbor encode");
196 let decoded: Body = Cbor::decode(&cbor).expect("cbor");
197 assert_eq!(decoded, body());
198 }
199
200 #[cfg(feature = "bson")]
201 #[test]
202 fn given_a_bson_payload_when_decoded_then_should_round_trip() {
203 let bson = Bson::encode(&body()).expect("bson encode");
204 let decoded: Body = Bson::decode(&bson).expect("bson");
205 assert_eq!(decoded, body());
206 assert_eq!(<Bson as Codec<Body>>::content_type(), ContentType::Bson);
207 }
208
209 #[test]
210 fn given_each_codec_when_encoding_then_should_advertise_its_content_type() {
211 assert_eq!(<Json as Codec<str>>::content_type(), ContentType::Json);
212 assert_eq!(
213 <Msgpack as Codec<str>>::content_type(),
214 ContentType::Msgpack
215 );
216 assert_eq!(<Cbor as Codec<str>>::content_type(), ContentType::Cbor);
217 }
218
219 #[test]
220 fn given_a_msgpack_value_when_round_tripped_through_the_entry_then_should_decode_back() {
221 let encoded = Msgpack::encode(&vec!["a", "b"]).expect("encode");
222 let entry = KvEntry {
223 key: b"k".to_vec(),
224 value: encoded,
225 expires_at_micros: None,
226 version: 0,
227 scope: None,
228 source: None,
229 };
230 let decoded: Vec<String> = entry.decode_value_with::<Msgpack, _>().expect("decode");
231 assert_eq!(decoded, vec!["a".to_owned(), "b".to_owned()]);
232 }
233}