use crate::Error;
use chrono::{DateTime, Utc};
use postcard::to_allocvec;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::convert::{Into, TryInto};
use std::time::Duration;
use std::fmt::Debug;
pub trait Message: Serialize + DeserializeOwned + Debug + Sync + Send + Clone {}
impl<T> Message for T where T: Serialize + DeserializeOwned + Debug + Sync + Send + Clone {}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[repr(C)]
pub enum MsgType {
Set,
Get,
GetNth(usize),
Topics,
Subscribe,
Result(Result<(), crate::Error>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[repr(C)]
pub struct Msg<T> {
pub msg_type: MsgType,
pub timestamp: DateTime<Utc>,
pub topic: String,
pub data_type: String,
pub data: T,
}
impl<T: Message> Msg<T> {
pub fn new(msg_type: MsgType, topic: impl Into<String>, data: T) -> Self {
Msg {
msg_type,
timestamp: Utc::now(),
topic: topic.into(),
data_type: std::any::type_name::<T>().to_string(),
data,
}
}
pub fn set_topic(&mut self, topic: impl Into<String>) {
self.topic = topic.into();
}
pub fn set_timestamp(&mut self, timestamp: DateTime<Utc>) {
self.timestamp = timestamp;
}
pub fn set_data(&mut self, data: T) {
self.data = data;
}
pub fn to_generic(self) -> Result<GenericMsg, crate::Error> {
self.try_into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[repr(C)]
pub struct GenericMsg {
pub msg_type: MsgType,
pub timestamp: DateTime<Utc>,
pub topic: String,
pub data_type: String,
pub data: Vec<u8>,
}
impl GenericMsg {
#[inline]
pub fn set<T: Message>(topic: impl Into<String>, data: Vec<u8>) -> Self {
GenericMsg {
msg_type: MsgType::Set,
timestamp: Utc::now(),
topic: topic.into(),
data_type: std::any::type_name::<T>().to_string(),
data,
}
}
#[inline]
pub fn get<T: Message>(topic: impl Into<String>) -> Self {
GenericMsg {
msg_type: MsgType::Get,
timestamp: Utc::now(),
topic: topic.into(),
data_type: std::any::type_name::<T>().to_string(),
data: Vec::new(),
}
}
#[inline]
pub fn subscribe(topic: impl Into<String>, rate: Duration) -> Result<Self, Error> {
let msg = Msg::new(MsgType::Subscribe, topic, rate);
msg.to_generic()
}
#[inline]
pub fn get_nth<T: Message>(topic: impl Into<String>, n: usize) -> Self {
GenericMsg {
msg_type: MsgType::GetNth(n),
timestamp: Utc::now(),
topic: topic.into(),
data_type: std::any::type_name::<T>().to_string(),
data: Vec::new(),
}
}
#[inline]
pub fn topics() -> Self {
GenericMsg {
msg_type: MsgType::Topics,
timestamp: Utc::now(),
topic: String::new(),
data_type: std::any::type_name::<()>().to_string(),
data: Vec::new(),
}
}
pub fn result(r: Result<(), Error>) -> Self {
GenericMsg {
msg_type: MsgType::Result(r),
timestamp: Utc::now(),
topic: String::new(),
data_type: std::any::type_name::<()>().to_string(),
data: Vec::new(),
}
}
pub fn set_data(&mut self, data: Vec<u8>) {
self.data = data;
}
pub fn as_bytes(&self) -> Result<Vec<u8>, postcard::Error> {
postcard::to_allocvec(&self)
}
}
impl<T: Message> TryInto<Msg<T>> for GenericMsg {
type Error = crate::Error;
fn try_into(self) -> Result<Msg<T>, Error> {
let data = postcard::from_bytes::<T>(&self.data[..])?;
Ok(Msg {
msg_type: self.msg_type,
timestamp: self.timestamp,
topic: self.topic.clone(),
data_type: self.data_type.clone(),
data,
})
}
}
impl<T: Message> TryInto<GenericMsg> for Msg<T> {
type Error = crate::Error;
fn try_into(self) -> Result<GenericMsg, Error> {
let data = postcard::to_allocvec(&self.data)?;
Ok(GenericMsg {
msg_type: self.msg_type,
timestamp: self.timestamp,
topic: self.topic.clone(),
data_type: self.data_type.clone(),
data,
})
}
}
#[test]
fn msg_conversions() {
let msg: Msg<i32> = Msg::new(MsgType::Set, "value", 0);
let generic = msg.clone().to_generic().unwrap();
let bytes = generic.clone().as_bytes().unwrap();
let generic_rc: GenericMsg = postcard::from_bytes(&bytes).unwrap();
dbg!(&generic_rc);
assert_eq!(generic, generic_rc);
let msg_rc: Msg<i32> = generic_rc.try_into().unwrap();
assert_eq!(msg, msg_rc);
}