use std::fmt;
use serde::Serialize;
use serde::de::DeserializeOwned;
#[derive(Debug)]
pub enum CodecError {
Encode(Box<dyn std::error::Error + Send + Sync>),
Decode(Box<dyn std::error::Error + Send + Sync>),
}
impl fmt::Display for CodecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CodecError::Encode(e) => write!(f, "encode error: {}", e),
CodecError::Decode(e) => write!(f, "decode error: {}", e),
}
}
}
impl std::error::Error for CodecError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
CodecError::Encode(e) => Some(e.as_ref()),
CodecError::Decode(e) => Some(e.as_ref()),
}
}
}
pub trait MessageCodec: Clone + 'static {
fn encode<T: Serialize>(&self, msg: &T) -> Result<Vec<u8>, CodecError>;
fn decode<T: DeserializeOwned>(&self, buf: &[u8]) -> Result<T, CodecError>;
}
#[derive(Clone, Default, Debug, Copy)]
pub struct JsonCodec;
impl MessageCodec for JsonCodec {
fn encode<T: Serialize>(&self, msg: &T) -> Result<Vec<u8>, CodecError> {
serde_json::to_vec(msg).map_err(|e| CodecError::Encode(Box::new(e)))
}
fn decode<T: DeserializeOwned>(&self, buf: &[u8]) -> Result<T, CodecError> {
serde_json::from_slice(buf).map_err(|e| CodecError::Decode(Box::new(e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct TestMessage {
id: u32,
content: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct NestedMessage {
outer: String,
inner: TestMessage,
}
#[test]
fn test_json_codec_roundtrip() {
let codec = JsonCodec;
let msg = TestMessage {
id: 42,
content: "hello world".to_string(),
};
let bytes = codec.encode(&msg).expect("encode should succeed");
let decoded: TestMessage = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(msg, decoded);
}
#[test]
fn test_json_codec_nested() {
let codec = JsonCodec;
let msg = NestedMessage {
outer: "outer".to_string(),
inner: TestMessage {
id: 1,
content: "inner".to_string(),
},
};
let bytes = codec.encode(&msg).expect("encode should succeed");
let decoded: NestedMessage = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(msg, decoded);
}
#[test]
fn test_json_codec_primitives() {
let codec = JsonCodec;
let s = "test string".to_string();
let bytes = codec.encode(&s).expect("encode should succeed");
let decoded: String = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(s, decoded);
let n = 12345u64;
let bytes = codec.encode(&n).expect("encode should succeed");
let decoded: u64 = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(n, decoded);
let v = vec![1, 2, 3, 4, 5];
let bytes = codec.encode(&v).expect("encode should succeed");
let decoded: Vec<i32> = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(v, decoded);
}
#[test]
fn test_json_codec_decode_error() {
let codec = JsonCodec;
let invalid_json = b"not valid json {";
let result: Result<TestMessage, CodecError> = codec.decode(invalid_json);
assert!(result.is_err());
let err = result.expect_err("decode should have failed");
assert!(matches!(err, CodecError::Decode(_)));
assert!(err.to_string().contains("decode error"));
}
#[test]
fn test_json_codec_type_mismatch() {
let codec = JsonCodec;
let msg = TestMessage {
id: 42,
content: "hello".to_string(),
};
let bytes = codec.encode(&msg).expect("encode should succeed");
let result: Result<String, CodecError> = codec.decode(&bytes);
assert!(result.is_err());
}
#[test]
fn test_codec_error_display() {
let encode_err = CodecError::Encode(Box::new(std::io::Error::other("test encode error")));
assert!(encode_err.to_string().contains("encode error"));
let decode_err = CodecError::Decode(Box::new(std::io::Error::other("test decode error")));
assert!(decode_err.to_string().contains("decode error"));
}
#[test]
fn test_json_codec_is_clone() {
let codec1 = JsonCodec;
let codec2 = codec1;
let msg = TestMessage {
id: 1,
content: "test".to_string(),
};
let bytes1 = codec1.encode(&msg).expect("encode should succeed");
let bytes2 = codec2.encode(&msg).expect("encode should succeed");
assert_eq!(bytes1, bytes2);
}
#[test]
fn test_json_codec_default() {
let codec = JsonCodec;
let msg = TestMessage {
id: 99,
content: "default".to_string(),
};
let bytes = codec.encode(&msg).expect("encode should succeed");
let decoded: TestMessage = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(msg, decoded);
}
#[test]
fn test_json_codec_empty_struct() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Empty {}
let codec = JsonCodec;
let msg = Empty {};
let bytes = codec.encode(&msg).expect("encode should succeed");
assert_eq!(&bytes, b"{}");
let decoded: Empty = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(msg, decoded);
}
#[test]
fn test_json_codec_option() {
let codec = JsonCodec;
let some_val: Option<i32> = Some(42);
let bytes = codec.encode(&some_val).expect("encode should succeed");
let decoded: Option<i32> = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(some_val, decoded);
let none_val: Option<i32> = None;
let bytes = codec.encode(&none_val).expect("encode should succeed");
let decoded: Option<i32> = codec.decode(&bytes).expect("decode should succeed");
assert_eq!(none_val, decoded);
}
}