Skip to main content

pamoja_codec/
json.rs

1//! A human-readable JSON codec built on [`serde_json`].
2
3use alloc::string::ToString;
4use alloc::vec::Vec;
5
6use serde::de::DeserializeOwned;
7use serde::Serialize;
8
9use pamoja_core::{Error, Result};
10
11use crate::Codec;
12
13/// A codec that serializes values to and from JSON.
14///
15/// JSON trades the compactness of CBOR for human readability, which makes it a
16/// good fit for debugging, configuration, and interop with services that speak
17/// JSON. The codec works for any type that implements [`serde::Serialize`] and
18/// [`serde::de::DeserializeOwned`].
19///
20/// # Examples
21///
22/// ```
23/// use pamoja_codec::{Codec, JsonCodec};
24///
25/// let codec = JsonCodec;
26/// let encoded = codec.encode(&vec![1, 2, 3]).unwrap();
27/// assert_eq!(encoded, b"[1,2,3]");
28/// let decoded: Vec<i32> = codec.decode(&encoded).unwrap();
29/// assert_eq!(decoded, vec![1, 2, 3]);
30/// ```
31#[derive(Clone, Copy, Debug, Default)]
32pub struct JsonCodec;
33
34impl<T> Codec<T> for JsonCodec
35where
36    T: Serialize + DeserializeOwned,
37{
38    fn encode(&self, value: &T) -> Result<Vec<u8>> {
39        serde_json::to_vec(value).map_err(|error| Error::Codec(error.to_string()))
40    }
41
42    fn decode(&self, bytes: &[u8]) -> Result<T> {
43        serde_json::from_slice(bytes).map_err(|error| Error::Codec(error.to_string()))
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn round_trips_a_map() {
53        use std::collections::BTreeMap;
54
55        let codec = JsonCodec;
56        let mut value = BTreeMap::new();
57        value.insert("sensor".to_owned(), 21i64);
58        let encoded = codec.encode(&value).expect("encode");
59        let decoded: BTreeMap<String, i64> = codec.decode(&encoded).expect("decode");
60        assert_eq!(decoded, value);
61    }
62
63    #[test]
64    fn decoding_invalid_json_is_a_codec_error() {
65        let codec = JsonCodec;
66        let result: Result<Vec<i32>> = codec.decode(b"not json");
67        assert!(matches!(result, Err(Error::Codec(_))));
68    }
69}