cpop_protocol/codec/
json.rs1use serde::{de::DeserializeOwned, Serialize};
8use std::io::{Read, Write};
9
10use super::{CodecError, Result};
11
12pub fn encode<T: Serialize>(value: &T) -> Result<Vec<u8>> {
14 serde_json::to_vec_pretty(value).map_err(|e| CodecError::JsonEncode(e.to_string()))
15}
16
17pub fn encode_compact<T: Serialize>(value: &T) -> Result<Vec<u8>> {
19 serde_json::to_vec(value).map_err(|e| CodecError::JsonEncode(e.to_string()))
20}
21
22pub fn decode<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
24 serde_json::from_slice(data).map_err(|e| CodecError::JsonDecode(e.to_string()))
25}
26
27pub fn encode_to<T: Serialize, W: Write>(value: &T, mut writer: W) -> Result<()> {
29 let bytes = encode(value)?;
30 writer.write_all(&bytes)?;
31 Ok(())
32}
33
34pub fn encode_to_compact<T: Serialize, W: Write>(value: &T, mut writer: W) -> Result<()> {
36 let bytes = encode_compact(value)?;
37 writer.write_all(&bytes)?;
38 Ok(())
39}
40
41pub fn decode_from<T: DeserializeOwned, R: Read>(reader: R) -> Result<T> {
43 serde_json::from_reader(reader).map_err(|e| CodecError::JsonDecode(e.to_string()))
44}
45
46pub fn to_string<T: Serialize>(value: &T) -> Result<String> {
48 serde_json::to_string_pretty(value).map_err(|e| CodecError::JsonEncode(e.to_string()))
49}
50
51pub fn to_string_compact<T: Serialize>(value: &T) -> Result<String> {
53 serde_json::to_string(value).map_err(|e| CodecError::JsonEncode(e.to_string()))
54}
55
56pub fn from_string<T: DeserializeOwned>(s: &str) -> Result<T> {
58 serde_json::from_str(s).map_err(|e| CodecError::JsonDecode(e.to_string()))
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use serde::{Deserialize, Serialize};
65
66 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67 struct TestData {
68 name: String,
69 count: u32,
70 items: Vec<String>,
71 }
72
73 #[test]
74 fn test_json_roundtrip() {
75 let original = TestData {
76 name: "test".to_string(),
77 count: 42,
78 items: vec!["a".to_string(), "b".to_string()],
79 };
80
81 let encoded = encode(&original).unwrap();
82 let decoded: TestData = decode(&encoded).unwrap();
83
84 assert_eq!(original, decoded);
85 }
86
87 #[test]
88 fn test_json_string_roundtrip() {
89 let original = TestData {
90 name: "string_test".to_string(),
91 count: 100,
92 items: vec!["x".to_string()],
93 };
94
95 let json_string = to_string(&original).unwrap();
96 let decoded: TestData = from_string(&json_string).unwrap();
97
98 assert_eq!(original, decoded);
99 }
100
101 #[test]
102 fn test_compact_vs_pretty() {
103 let data = TestData {
104 name: "compact".to_string(),
105 count: 1,
106 items: vec![],
107 };
108
109 let pretty = encode(&data).unwrap();
110 let compact = encode_compact(&data).unwrap();
111
112 assert!(compact.len() < pretty.len());
113
114 let decoded_pretty: TestData = decode(&pretty).unwrap();
115 let decoded_compact: TestData = decode(&compact).unwrap();
116
117 assert_eq!(decoded_pretty, decoded_compact);
118 }
119
120 #[test]
121 fn test_decode_invalid_json() {
122 let garbage = b"not valid json {{{";
123 let result: Result<TestData> = decode(garbage);
124 assert!(matches!(result, Err(CodecError::JsonDecode(_))));
125 }
126
127 #[test]
128 fn test_encode_to_decode_from_json() {
129 let original = TestData {
130 name: "stream".to_string(),
131 count: 55,
132 items: vec!["one".to_string(), "two".to_string()],
133 };
134
135 let mut buf = Vec::new();
136 encode_to(&original, &mut buf).unwrap();
137 let decoded: TestData = decode_from(&buf[..]).unwrap();
138 assert_eq!(original, decoded);
139 }
140
141 #[test]
142 fn test_encode_to_compact_writer() {
143 let data = TestData {
144 name: "cw".to_string(),
145 count: 0,
146 items: vec![],
147 };
148
149 let mut pretty_buf = Vec::new();
150 encode_to(&data, &mut pretty_buf).unwrap();
151
152 let mut compact_buf = Vec::new();
153 encode_to_compact(&data, &mut compact_buf).unwrap();
154
155 assert!(compact_buf.len() < pretty_buf.len());
156
157 let decoded: TestData = decode(&compact_buf).unwrap();
158 assert_eq!(data, decoded);
159 }
160
161 #[test]
162 fn test_from_string_invalid() {
163 let result: Result<TestData> = from_string("}{bad");
164 assert!(matches!(result, Err(CodecError::JsonDecode(_))));
165 }
166
167 #[test]
168 fn test_to_string_compact_roundtrip() {
169 let data = TestData {
170 name: "compact_str".to_string(),
171 count: 999,
172 items: vec!["z".to_string()],
173 };
174
175 let compact = to_string_compact(&data).unwrap();
176 assert!(!compact.contains('\n'));
177
178 let decoded: TestData = from_string(&compact).unwrap();
179 assert_eq!(data, decoded);
180 }
181}