//! Encoding / Decoding utilities.
/// Encode a serde::Serialize item as message-pack data to given writer.
/// You may wish to first wrap your writer in a BufWriter.
pub fn rmp_encode<W, S>(write: &mut W, item: S) -> Result<(), std::io::Error>
where
W: std::io::Write,
S: serde::Serialize,
{
let mut se = rmp_serde::encode::Serializer::new(write)
.with_struct_map()
.with_string_variants();
item.serialize(&mut se)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
Ok(())
}
/// Decode message-pack data from given reader into an owned item.
/// You may wish to first wrap your reader in a BufReader.
pub fn rmp_decode<R, D>(r: &mut R) -> Result<D, std::io::Error>
where
R: std::io::Read,
for<'de> D: Sized + serde::Deserialize<'de>,
{
let mut de = rmp_serde::decode::Deserializer::new(r);
D::deserialize(&mut de).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
}
/// Apply to a data item to indicate it can be encoded / decoded.
pub trait Codec: Clone + Sized {
/// Variant identifier (for debugging or as a cheap discriminant).
fn variant_type(&self) -> &'static str;
/// Encode this item to given writer.
/// You may wish to first wrap your writer in a BufWriter.
fn encode<W>(&self, w: &mut W) -> Result<(), std::io::Error>
where
W: std::io::Write;
/// Encode this item to an owned vector of bytes.
/// Uses `encode()` internally.
fn encode_vec(&self) -> Result<Vec<u8>, std::io::Error> {
let mut data = Vec::new();
self.encode(&mut data)?;
Ok(data)
}
/// Decode a reader into this item.
/// You may wish to first wrap your reader in a BufReader.
fn decode<R>(r: &mut R) -> Result<Self, std::io::Error>
where
R: std::io::Read;
/// Decode a range of bytes into this item.
/// Will also return the byte count read.
/// Uses `decode()` internally.
fn decode_ref(r: &[u8]) -> Result<(u64, Self), std::io::Error> {
let mut r = std::io::Cursor::new(r);
let item = Self::decode(&mut r)?;
Ok((r.position(), item))
}
}
/// DSL-style macro for generating a serialization protocol message enum.
///
/// DSL:
///
/// ```ignore
/// /// [codec doc here]
/// codec $codec_name {
/// /// [var doc here]
/// $var_name($var_id) {
/// /// [type doc here]
/// $type_name.$type_idx: $type_ty,
/// },
/// }
/// ```
///
/// - $codec_name - camel-case codec enum name
/// - $var_name - camel-case variant/struct name
/// - $var_id - protocol variant identifier byte (u8) literal
/// - $type_name - snake-case type name
/// - $type_idx - zero-index type index in message array (usize)
/// - $type_ty - type rust type
///
/// E.G.:
///
/// ```ignore
/// /// My codec is awesome.
/// codec MyCodec {
/// /// My codec has only one variant.
/// MyVariant(0x00) {
/// /// My variant has only one type
/// my_type.0: String,
/// },
/// }
/// ```
#[macro_export]
macro_rules! write_codec_enum {
($(#[doc = $codec_doc:expr])* codec $codec_name:ident {$(
$(#[doc = $var_doc:expr])* $var_name:ident($var_id:literal) {$(
$(#[doc = $type_doc:expr])* $type_name:ident.$type_idx:literal: $type_ty:ty,
)*},
)*}) => {
$crate::dependencies::paste::item! {
$(
$(#[doc = $var_doc])*
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct [< $var_name:camel >] {
$(
$(#[doc = $type_doc])* pub [< $type_name:snake >]: $type_ty,
)*
}
impl $crate::codec::Codec for [< $var_name:camel >] {
fn variant_type(&self) -> &'static str {
concat!(
stringify!([< $codec_name:camel >]),
"::",
stringify!([< $var_name:camel >]),
)
}
fn encode<W>(&self, w: &mut W) -> ::std::io::Result<()>
where
W: ::std::io::Write
{
#[cfg(debug_assertions)]
#[allow(dead_code)]
{
const MSG: &str = "type index must begin at 0 and increment by exactly 1 per type - switching type order will break parsing compatibility";
let mut _idx = -1;
$(
_idx += 1;
assert_eq!(_idx, $type_idx, "{}", MSG);
)*
}
let t: (
$(&$type_ty,)*
) = (
$(&self.[< $type_name:snake >],)*
);
$crate::codec::rmp_encode(w, &t)
}
fn decode<R>(r: &mut R) -> ::std::io::Result<Self>
where
R: ::std::io::Read
{
let (
$([< $type_name:snake >],)*
): (
$($type_ty,)*
) = $crate::codec::rmp_decode(r)?;
Ok([< $var_name:camel >] {
$(
[< $type_name:snake >],
)*
})
}
}
)*
$(#[doc = $codec_doc])*
#[derive(Clone, Debug, PartialEq)]
pub enum [< $codec_name:camel >] {
$(
$(#[doc = $var_doc])*
[< $var_name:camel >]([< $var_name:camel >]),
)*
}
impl [< $codec_name:camel >] {
$(
/// Variant constructor helper function.
pub fn [< $var_name:snake >]($(
[< $type_name:snake >]: $type_ty,
)*) -> Self {
Self::[< $var_name:camel >]([< $var_name:camel >] {
$(
[< $type_name:snake >],
)*
})
}
)*
}
impl $crate::codec::Codec for [< $codec_name:camel >] {
fn variant_type(&self) -> &'static str {
match self {
$(
Self::[< $var_name:camel >](data) =>
$crate::codec::Codec::variant_type(data),
)*
}
}
fn encode<W>(&self, w: &mut W) -> ::std::io::Result<()>
where
W: ::std::io::Write
{
match self {
$(
Self::[< $var_name:camel >](data) => {
::std::io::Write::write_all(w, &[$var_id])?;
$crate::codec::Codec::encode(data, w)
}
)*
}
}
fn decode<R>(r: &mut R) -> Result<Self, ::std::io::Error>
where
R: ::std::io::Read
{
let mut c = [0_u8; 1];
::std::io::Read::read_exact(r, &mut c)?;
match c[0] {
$(
$var_id => {
Ok(Self::[< $var_name:camel >]($crate::codec::Codec::decode(r)?))
},
)*
_ => Err(::std::io::Error::new(::std::io::ErrorKind::Other, "invalid protocol byte")),
}
}
}
}
};
}
#[cfg(test)]
mod tests {
#![allow(dead_code)]
use super::*;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Sub(pub Vec<u8>);
write_codec_enum! {
/// Codec
codec Bob {
/// variant
BobOne(0x42) {
/// type 1
yay.0: bool,
/// type 2
age.1: u32,
/// type 3
sub.2: Arc<Sub>,
},
/// nother variant
BobTwo(0x43) {
},
}
}
#[test]
fn test_encode_decode() {
let bob = Bob::bob_one(true, 42, Arc::new(Sub(b"test".to_vec())));
let data = bob.encode_vec().unwrap();
let res = Bob::decode_ref(&data).unwrap().1;
assert_eq!(bob, res);
}
}