use alloc::vec::Vec;
use serde::{de::DeserializeOwned, Serialize};
pub trait RemoteSerialize: Sized {
fn to_json(&self) -> Option<serde_json::Value>;
fn from_json(value: &serde_json::Value) -> Option<Self>;
fn to_json_vec(&self) -> Option<Vec<u8>> {
serde_json::to_vec(&self.to_json()?).ok()
}
fn from_json_slice(bytes: &[u8]) -> Option<Self> {
let value = serde_json::from_slice(bytes).ok()?;
Self::from_json(&value)
}
}
impl<T> RemoteSerialize for T
where
T: Serialize + DeserializeOwned,
{
fn to_json(&self) -> Option<serde_json::Value> {
serde_json::to_value(self).ok()
}
fn from_json(value: &serde_json::Value) -> Option<Self> {
serde_json::from_value(value.clone()).ok()
}
fn to_json_vec(&self) -> Option<Vec<u8>> {
serde_json::to_vec(self).ok()
}
fn from_json_slice(bytes: &[u8]) -> Option<Self> {
serde_json::from_slice(bytes).ok()
}
}
pub trait JsonCodec<T>: Send + Sync {
fn encode(&self, value: &T) -> Option<serde_json::Value>;
fn decode(&self, value: &serde_json::Value) -> Option<T>;
fn encode_to_vec(&self, value: &T) -> Option<Vec<u8>> {
serde_json::to_vec(&self.encode(value)?).ok()
}
fn decode_slice(&self, bytes: &[u8]) -> Option<T> {
let value = serde_json::from_slice(bytes).ok()?;
self.decode(&value)
}
}
pub struct SerdeJsonCodec;
impl<T: RemoteSerialize> JsonCodec<T> for SerdeJsonCodec {
fn encode(&self, value: &T) -> Option<serde_json::Value> {
value.to_json()
}
fn decode(&self, value: &serde_json::Value) -> Option<T> {
T::from_json(value)
}
fn encode_to_vec(&self, value: &T) -> Option<Vec<u8>> {
value.to_json_vec()
}
fn decode_slice(&self, bytes: &[u8]) -> Option<T> {
T::from_json_slice(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::String;
use alloc::vec;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Fixture {
name: String,
optional: Option<u32>,
bytes: Vec<u8>,
}
struct TreeOnlyCodec;
impl JsonCodec<Fixture> for TreeOnlyCodec {
fn encode(&self, value: &Fixture) -> Option<serde_json::Value> {
serde_json::to_value(value).ok()
}
fn decode(&self, value: &serde_json::Value) -> Option<Fixture> {
serde_json::from_value(value.clone()).ok()
}
}
#[derive(Debug, PartialEq)]
struct ManualRemote(u64);
impl RemoteSerialize for ManualRemote {
fn to_json(&self) -> Option<serde_json::Value> {
Some(json!({ "value": self.0 }))
}
fn from_json(value: &serde_json::Value) -> Option<Self> {
Some(Self(value.get("value")?.as_u64()?))
}
}
fn fixture() -> Fixture {
Fixture {
name: "cảm biến \"ngoài trời\"".into(),
optional: None,
bytes: vec![0, 1, 127, 255],
}
}
#[test]
fn tree_only_codec_gets_default_byte_compatibility() {
let value = fixture();
let bytes = TreeOnlyCodec.encode_to_vec(&value).unwrap();
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&bytes).unwrap(),
TreeOnlyCodec.encode(&value).unwrap()
);
assert_eq!(TreeOnlyCodec.decode_slice(&bytes), Some(value));
}
#[test]
fn manual_remote_serialize_gets_default_byte_compatibility() {
let value = ManualRemote(42);
let bytes =
<SerdeJsonCodec as JsonCodec<ManualRemote>>::encode_to_vec(&SerdeJsonCodec, &value)
.unwrap();
assert_eq!(bytes, br#"{"value":42}"#);
assert_eq!(
<SerdeJsonCodec as JsonCodec<ManualRemote>>::decode_slice(&SerdeJsonCodec, &bytes),
Some(value)
);
}
#[test]
fn serde_codec_byte_path_matches_json_value_semantics() {
let value = fixture();
let bytes =
<SerdeJsonCodec as JsonCodec<Fixture>>::encode_to_vec(&SerdeJsonCodec, &value).unwrap();
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&bytes).unwrap(),
<SerdeJsonCodec as JsonCodec<Fixture>>::encode(&SerdeJsonCodec, &value).unwrap()
);
assert_eq!(
<SerdeJsonCodec as JsonCodec<Fixture>>::decode_slice(&SerdeJsonCodec, &bytes),
Some(value)
);
}
#[test]
fn serde_codec_rejects_malformed_trailing_and_mismatched_bytes() {
for invalid in [
br#"{"name":"truncated""#.as_slice(),
br#"{"name":"ok","optional":null,"bytes":[]} trailing"#.as_slice(),
br#"{"name":7,"optional":null,"bytes":[]}"#.as_slice(),
] {
assert!(
<SerdeJsonCodec as JsonCodec<Fixture>>::decode_slice(&SerdeJsonCodec, invalid)
.is_none()
);
}
}
}