#[cfg(feature = "serde")]
pub mod bit_codec;
pub mod bits;
pub mod envelope;
#[cfg(feature = "serde")]
pub mod flow_payloads;
pub mod frame;
pub mod framing;
#[cfg(feature = "serde")]
pub mod payload;
#[cfg(feature = "serde")]
pub mod payloads;
#[cfg(feature = "build")]
pub mod build;
#[cfg(feature = "serde")]
pub use bit_codec::BitCodec;
pub use envelope::{Agent, AgentId};
#[cfg(feature = "serde")]
pub use payload::Payload;
pub use frame::Kind;
#[cfg(feature = "serde")]
use serde::de::DeserializeOwned;
#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg(feature = "serde")]
pub trait WireCodec: Clone + Send + Sync + 'static {
fn name(&self) -> &'static str;
fn encode<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
fn encode_into<T: Serialize>(
&self,
value: &T,
buf: &mut Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
buf.extend_from_slice(&self.encode(value)?);
Ok(())
}
fn decode<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, Box<dyn std::error::Error + Send + Sync>>;
fn is_text(&self) -> bool {
false
}
}
#[cfg(feature = "json")]
#[derive(Debug, Clone, Copy, Default)]
pub struct JsonCodec;
#[cfg(feature = "json")]
impl WireCodec for JsonCodec {
fn name(&self) -> &'static str {
"json"
}
fn encode<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
serde_json::to_vec(value).map_err(Into::into)
}
fn encode_into<T: Serialize>(
&self,
value: &T,
buf: &mut Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
serde_json::to_writer(buf, value).map_err(Into::into)
}
fn decode<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
serde_json::from_slice(bytes).map_err(Into::into)
}
fn is_text(&self) -> bool {
true
}
}
#[cfg(feature = "msgpack")]
#[derive(Debug, Clone, Copy, Default)]
pub struct MsgPackCodec;
#[cfg(feature = "msgpack")]
impl WireCodec for MsgPackCodec {
fn name(&self) -> &'static str {
"msgpack"
}
fn encode<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::to_vec(value).map_err(Into::into)
}
fn encode_into<T: Serialize>(
&self,
value: &T,
buf: &mut Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::encode::write(buf, value).map_err(Into::into)
}
fn decode<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::from_slice(bytes).map_err(Into::into)
}
}
#[cfg(feature = "msgpack")]
#[derive(Debug, Clone, Copy, Default)]
pub struct MsgPackNamedCodec;
#[cfg(feature = "msgpack")]
impl WireCodec for MsgPackNamedCodec {
fn name(&self) -> &'static str {
"msgpack-named"
}
fn encode<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::to_vec_named(value).map_err(Into::into)
}
fn encode_into<T: Serialize>(
&self,
value: &T,
buf: &mut Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::encode::write_named(buf, value).map_err(Into::into)
}
fn decode<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
rmp_serde::from_slice(bytes).map_err(Into::into)
}
}
#[cfg(all(test, feature = "json"))]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Move {
player: u32,
dx: f32,
}
#[test]
fn a_value_survives_a_round_trip() {
let codec = JsonCodec;
let original = Move { player: 7, dx: -1.5 };
let bytes = codec.encode(&original).unwrap();
let decoded: Move = codec.decode(&bytes).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn decoding_the_wrong_shape_is_an_error_not_a_panic() {
let codec = JsonCodec;
let bytes = codec.encode(&"not a move").unwrap();
assert!(codec.decode::<Move>(&bytes).is_err());
}
#[test]
fn the_name_reaches_error_messages() {
assert_eq!(JsonCodec.name(), "json");
}
#[cfg(feature = "msgpack")]
#[test]
fn named_carries_the_field_names_and_compact_does_not() {
let value = Move { player: 7, dx: -1.5 };
let compact = MsgPackCodec.encode(&value).unwrap();
let named = MsgPackNamedCodec.encode(&value).unwrap();
assert!(!compact.windows(6).any(|w| w == b"player"));
assert!(named.windows(6).any(|w| w == b"player"));
assert!(named.len() > compact.len(), "the names are what named pays for");
}
#[cfg(feature = "msgpack")]
#[test]
fn either_msgpack_codec_decodes_the_other() {
let value = Move { player: 7, dx: -1.5 };
let compact = MsgPackCodec.encode(&value).unwrap();
let named = MsgPackNamedCodec.encode(&value).unwrap();
assert_eq!(MsgPackNamedCodec.decode::<Move>(&compact).unwrap(), value);
assert_eq!(MsgPackCodec.decode::<Move>(&named).unwrap(), value);
}
#[cfg(feature = "msgpack")]
#[test]
fn encode_into_appends_what_encode_returns() {
let value = Move { player: 7, dx: -1.5 };
let mut buf = vec![0xAB];
MsgPackNamedCodec.encode_into(&value, &mut buf).unwrap();
assert_eq!(buf[0], 0xAB);
assert_eq!(&buf[1..], &MsgPackNamedCodec.encode(&value).unwrap()[..]);
}
}