Expand description
Provides a generic module for use with #[serde(with = "...")] to serialize and deserialize
custom types as the MessagePack Ext format.
This module is used with serde’s with attribute to easily serialize and deserialize
custom types that implement the EncodeExt and DecodeExt traits into the MessagePack
Ext format.
Note: When the chrono feature is enabled, built-in implementations of EncodeExt and
DecodeExt are provided for chrono::DateTime<Utc> and chrono::NaiveDateTime. This allows
these types to be serialized and deserialized as MessagePack’s Timestamp format (which is an
ext type) using #[serde(with = "shmp::ext_format")].
§Example
Let’s say you have a custom type MyCustomType that you want to serialize as an Ext format.
The data part of the Ext can be any custom binary format you define.
use serde::{Serialize, Deserialize};
use shmp::{EncodeExt, DecodeExt, to_vec, from_slice, Error};
// 1. Define your custom type to be handled as an Ext format.
#[derive(Debug, PartialEq)]
struct MyCustomType {
id: u32,
value: String,
}
// 2. Implement the `EncodeExt` trait to define the type's unique tag and serialization logic.
impl EncodeExt for MyCustomType {
// Choose a unique tag from -128 to 127 to identify this type.
const EXT_TAG: i8 = 42;
fn to_ext_data(&self) -> Result<Vec<u8>, Error> {
// Convert the internal data into a byte vector using your custom format.
// Here, we'll simply concatenate the big-endian bytes of `id` and the UTF-8 bytes of `value`.
let mut bytes = self.id.to_be_bytes().to_vec();
bytes.extend_from_slice(self.value.as_bytes());
Ok(bytes)
}
}
// 3. Implement the `DecodeExt` trait to define the deserialization logic.
impl DecodeExt for MyCustomType {
const EXT_TAG: i8 = 42; // Must match the tag in `EncodeExt`.
fn from_ext_data(data: &[u8]) -> Result<Self, Error> {
// Restore the original type from the byte slice.
if data.len() < 4 {
return Err(Error::DeserializeError("Not enough data for MyCustomType id".into()));
}
let id = u32::from_be_bytes(data[0..4].try_into().unwrap());
let value = String::from_utf8(data[4..].to_vec())?;
Ok(MyCustomType { id, value})
}
}
// 4. Use `#[serde(with = "shmp::ext_format")]` on a field in another struct.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Packet {
sequence: u64,
#[serde(with = "shmp::ext_format")]
payload: MyCustomType,
}
// Now, when a `Packet` is serialized, the `payload` field will be encoded in the Ext format.
let packet = Packet {
sequence: 1,
payload: MyCustomType { id: 101, value: "hello".to_string() },
};
let encoded = to_vec(&packet).unwrap();
let decoded: Packet = from_slice(&encoded).unwrap();
assert_eq!(packet, decoded);