use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{stream, Channel, Contents};
use crate::error::Error;
#[derive(Clone, Debug, PartialEq)]
pub enum Message {
Send {
channel: Channel,
contents: Contents,
},
Receive {
channel: Channel,
contents: Contents,
},
StreamSend {
channel: Channel,
contents: stream::Contents,
},
StreamReceive {
channel: Channel,
contents: stream::Contents,
},
Subscribe {
channel: Channel,
},
Unsubscribe {
channel: Channel,
},
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Send { channel, contents } => {
write!(f, "{:<14} -> {contents}", channel.ident)
}
Self::Receive { channel, contents } => {
write!(f, "{:<14} <- {contents}", channel.ident)
}
Self::StreamSend { channel, contents } => {
write!(f, "{:<14} -> {contents}", channel.ident)
}
Self::StreamReceive { channel, contents } => {
write!(f, "{:<14} <- {contents}", channel.ident)
}
Self::Subscribe { channel } => write!(f, "subscribing to {channel}"),
Self::Unsubscribe { channel } => write!(f, "unsubscribing from {channel}"),
}
}
}
impl Serialize for Message {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let wire_message =
WireMessage::try_from(self.clone()).map_err(serde::ser::Error::custom)?;
wire_message.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Message {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let wire_message = WireMessage::deserialize(deserializer)?;
Self::try_from(wire_message).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
enum WireMessage {
WithContents(Stanza, Channel, Contents),
WithStreamContents(Stanza, Channel, stream::Contents),
Subscription(Stanza, Channel),
}
#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
enum Stanza {
#[serde(rename = "msg")]
Receive,
#[serde(rename = "send")]
Send,
#[serde(rename = "sub")]
Subscribe,
#[serde(rename = "unsub")]
Unsubscribe,
}
impl fmt::Display for Stanza {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl TryFrom<Message> for WireMessage {
type Error = Error;
fn try_from(message: Message) -> Result<Self, Self::Error> {
let wire_message = match message {
Message::Receive { channel, contents } => {
let contents_ident = contents.ident;
let channel_ident = channel.ident;
if contents_ident != channel_ident {
return Err(Self::Error::failed_precondition(format!(
"channel identifier {channel_ident} should match content identifier {contents_ident}",
)));
}
Self::WithContents(Stanza::Receive, channel, contents)
}
Message::Send { channel, contents } => {
let contents_ident = contents.ident;
let channel_ident = channel.ident;
if contents_ident != channel_ident {
return Err(Self::Error::failed_precondition(format!(
"channel identifier {channel_ident} should match content identifier {contents_ident}",
)));
}
Self::WithContents(Stanza::Send, channel, contents)
}
Message::StreamReceive { channel, contents } => {
Self::WithStreamContents(Stanza::Receive, channel, contents)
}
Message::StreamSend { channel, contents } => {
Self::WithStreamContents(Stanza::Send, channel, contents)
}
Message::Subscribe { channel } => Self::Subscription(Stanza::Subscribe, channel),
Message::Unsubscribe { channel } => Self::Subscription(Stanza::Unsubscribe, channel),
};
Ok(wire_message)
}
}
impl TryFrom<WireMessage> for Message {
type Error = Error;
fn try_from(wire_message: WireMessage) -> Result<Self, Self::Error> {
let message = match wire_message {
WireMessage::WithContents(stanza, channel, contents) => {
let contents_ident = contents.ident;
let channel_ident = channel.ident;
if contents_ident != channel_ident {
return Err(Self::Error::failed_precondition(format!(
"channel identifier {channel_ident} should match content identifier {contents_ident}",
)));
}
match stanza {
Stanza::Send => Self::Send { channel, contents },
Stanza::Receive => Self::Receive { channel, contents },
_ => {
return Err(Self::Error::failed_precondition(format!(
"stanza {stanza} should match for message with contents"
)));
}
}
}
WireMessage::WithStreamContents(stanza, channel, contents) => match stanza {
Stanza::Send => Self::StreamSend { channel, contents },
Stanza::Receive => Self::StreamReceive { channel, contents },
_ => {
return Err(Self::Error::failed_precondition(format!(
"stanza {stanza} should match for stream message with contents"
)));
}
},
WireMessage::Subscription(stanza, channel) => match stanza {
Stanza::Subscribe => Self::Subscribe { channel },
Stanza::Unsubscribe => Self::Unsubscribe { channel },
_ => {
return Err(Self::Error::failed_precondition(format!(
"stanza {stanza} should match for subscription message"
)));
}
},
};
Ok(message)
}
}