graphrefly_storage/
codec.rs1use serde::{de::DeserializeOwned, Serialize};
20use thiserror::Error;
21
22#[derive(Debug, Error)]
25pub enum CodecError {
26 #[error("codec encode failed: {0}")]
27 Encode(String),
28
29 #[error("codec decode failed: {0}")]
30 Decode(String),
31}
32
33pub trait Codec<T>: Send + Sync {
37 fn name(&self) -> &str;
39 fn version(&self) -> u32;
41 fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError>;
42 fn decode(&self, bytes: &[u8]) -> Result<T, CodecError>;
43}
44
45#[derive(Debug, Default, Clone, Copy)]
48pub struct JsonCodec;
49
50impl<T> Codec<T> for JsonCodec
51where
52 T: Serialize + DeserializeOwned + Send + Sync,
53{
54 fn name(&self) -> &'static str {
55 "json"
56 }
57 fn version(&self) -> u32 {
58 1
59 }
60 fn encode(&self, value: &T) -> Result<Vec<u8>, CodecError> {
61 let v = serde_json::to_value(value).map_err(|e| CodecError::Encode(e.to_string()))?;
64 serde_json::to_vec(&v).map_err(|e| CodecError::Encode(e.to_string()))
65 }
66 fn decode(&self, bytes: &[u8]) -> Result<T, CodecError> {
67 serde_json::from_slice(bytes).map_err(|e| CodecError::Decode(e.to_string()))
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use serde::Deserialize;
75
76 #[derive(Serialize, Deserialize, Debug, PartialEq)]
77 struct Counter {
78 zebra: u32,
79 apple: u32,
80 monkey: u32,
81 }
82
83 #[test]
84 fn json_codec_round_trip() {
85 let codec = JsonCodec;
86 let v = Counter {
87 zebra: 1,
88 apple: 2,
89 monkey: 3,
90 };
91 let bytes = <JsonCodec as Codec<Counter>>::encode(&codec, &v).unwrap();
92 let back: Counter = <JsonCodec as Codec<Counter>>::decode(&codec, &bytes).unwrap();
93 assert_eq!(v, back);
94 }
95
96 #[test]
97 fn json_codec_canonical_sorts_keys() {
98 let codec = JsonCodec;
99 let v = Counter {
100 zebra: 1,
101 apple: 2,
102 monkey: 3,
103 };
104 let bytes = <JsonCodec as Codec<Counter>>::encode(&codec, &v).unwrap();
105 let s = std::str::from_utf8(&bytes).unwrap();
106 assert_eq!(s, r#"{"apple":2,"monkey":3,"zebra":1}"#);
109 }
110
111 #[test]
112 fn json_codec_name_and_version() {
113 let codec = JsonCodec;
114 assert_eq!(<JsonCodec as Codec<Counter>>::name(&codec), "json");
115 assert_eq!(<JsonCodec as Codec<Counter>>::version(&codec), 1);
116 }
117
118 #[test]
119 fn json_codec_decode_rejects_invalid_bytes() {
120 let codec = JsonCodec;
121 let result: Result<Counter, _> = <JsonCodec as Codec<Counter>>::decode(&codec, b"not json");
122 assert!(matches!(result, Err(CodecError::Decode(_))));
123 }
124}