use microsandbox_protocol::wire::{self, Envelope};
use serde::{Serialize, de::DeserializeOwned};
use crate::{ClientError, ClientResult, EnvelopeCodec, ErrorKind, Protocol, RawFrame};
pub struct Message {
pub v: u8,
pub t: String,
pub id: u32,
pub flags: u8,
pub p: Vec<u8>,
frame: RawFrame,
}
pub struct TypedMessage<T> {
pub message_type: String,
pub payload: T,
}
pub struct EncodedMessage {
pub message_type: String,
pub payload: Vec<u8>,
}
pub struct OutboundMessage {
pub flags: u8,
pub body: Vec<u8>,
}
pub trait IntoOutboundMessage<P: Protocol> {
fn into_outbound(
self,
ready: &P::Ready,
codec: &dyn EnvelopeCodec,
) -> ClientResult<OutboundMessage>;
}
impl Message {
pub fn new(frame: RawFrame, envelope: Envelope) -> Self {
Self {
v: envelope.v,
t: envelope.t,
id: frame.id,
flags: frame.flags,
p: envelope.p,
frame,
}
}
pub fn payload<T: DeserializeOwned>(&self) -> ClientResult<T> {
let value = wire::decode_value(&self.p)?;
value
.deserialized()
.map_err(|_| ClientError::new(ErrorKind::InvalidData))
}
pub fn raw(&self) -> &RawFrame {
&self.frame
}
pub fn into_raw(self) -> RawFrame {
self.frame
}
}
impl<T> TypedMessage<T> {
pub fn new(message_type: impl AsRef<str>, payload: T) -> Self {
Self {
message_type: message_type.as_ref().into(),
payload,
}
}
}
impl EncodedMessage {
pub fn new(message_type: impl AsRef<str>, payload: impl Into<Vec<u8>>) -> Self {
Self {
message_type: message_type.as_ref().into(),
payload: payload.into(),
}
}
}
impl<P: Protocol, T: Serialize> IntoOutboundMessage<P> for TypedMessage<T> {
fn into_outbound(
self,
ready: &P::Ready,
codec: &dyn EnvelopeCodec,
) -> ClientResult<OutboundMessage> {
let metadata = P::prepare(ready, &self.message_type)?;
let payload =
wire::encode(&self.payload).map_err(|_| ClientError::new(ErrorKind::Encode))?;
let body = codec.encode(metadata.generation, &self.message_type, payload)?;
Ok(OutboundMessage {
flags: metadata.flags,
body,
})
}
}
impl<P: Protocol> IntoOutboundMessage<P> for EncodedMessage {
fn into_outbound(
self,
ready: &P::Ready,
codec: &dyn EnvelopeCodec,
) -> ClientResult<OutboundMessage> {
let metadata = P::prepare(ready, &self.message_type)?;
let body = codec.encode(metadata.generation, &self.message_type, self.payload)?;
Ok(OutboundMessage {
flags: metadata.flags,
body,
})
}
}
impl std::fmt::Debug for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Message")
.field("id", &self.id)
.field("flags", &self.flags)
.field("generation", &self.v)
.field("payload_bytes", &self.p.len())
.finish_non_exhaustive()
}
}