use std::collections::btree_map::Iter;
use std::collections::BTreeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::hash::Hash;
use crate::message::{MessageEncoded, MessageError, MessageFieldsError};
use crate::Validate;
#[derive(Clone, Debug, PartialEq, Serialize_repr, Deserialize_repr)]
#[serde(untagged)]
#[repr(u8)]
pub enum MessageVersion {
Default = 1,
}
impl Copy for MessageVersion {}
#[derive(Clone, Debug, PartialEq)]
pub enum MessageAction {
Create,
Update,
Delete,
}
impl Serialize for MessageAction {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(match *self {
MessageAction::Create => "create",
MessageAction::Update => "update",
MessageAction::Delete => "delete",
})
}
}
impl<'de> Deserialize<'de> for MessageAction {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"create" => Ok(MessageAction::Create),
"update" => Ok(MessageAction::Update),
"delete" => Ok(MessageAction::Delete),
_ => Err(serde::de::Error::custom("unknown message action")),
}
}
}
impl Copy for MessageAction {}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum MessageValue {
#[serde(rename = "bool")]
Boolean(bool),
#[serde(rename = "int")]
Integer(i64),
#[serde(rename = "float")]
Float(f64),
#[serde(rename = "str")]
Text(String),
#[serde(rename = "relation")]
Relation(Hash),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MessageFields(BTreeMap<String, MessageValue>);
impl MessageFields {
pub fn new() -> Self {
Self(BTreeMap::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn add(&mut self, name: &str, value: MessageValue) -> Result<(), MessageFieldsError> {
if self.0.contains_key(name) {
return Err(MessageFieldsError::FieldDuplicate);
}
self.0.insert(name.to_owned(), value);
Ok(())
}
pub fn update(&mut self, name: &str, value: MessageValue) -> Result<(), MessageFieldsError> {
if !self.0.contains_key(name) {
return Err(MessageFieldsError::UnknownField);
}
self.0.insert(name.to_owned(), value);
Ok(())
}
pub fn remove(&mut self, name: &str) -> Result<(), MessageFieldsError> {
if !self.0.contains_key(name) {
return Err(MessageFieldsError::UnknownField);
}
self.0.remove(name);
Ok(())
}
pub fn get(&self, name: &str) -> Option<&MessageValue> {
if !self.0.contains_key(name) {
return None;
}
self.0.get(name)
}
pub fn keys(&self) -> Vec<String> {
self.0.keys().cloned().collect()
}
pub fn iter(&self) -> Iter<String, MessageValue> {
self.0.iter()
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Message {
action: MessageAction,
schema: Hash,
version: MessageVersion,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<Hash>,
#[serde(skip_serializing_if = "Option::is_none")]
fields: Option<MessageFields>,
}
impl Message {
pub fn new_create(schema: Hash, fields: MessageFields) -> Result<Self, MessageError> {
let message = Self {
action: MessageAction::Create,
version: MessageVersion::Default,
schema,
id: None,
fields: Some(fields),
};
message.validate()?;
Ok(message)
}
pub fn new_update(schema: Hash, id: Hash, fields: MessageFields) -> Result<Self, MessageError> {
let message = Self {
action: MessageAction::Update,
version: MessageVersion::Default,
schema,
id: Some(id),
fields: Some(fields),
};
message.validate()?;
Ok(message)
}
pub fn new_delete(schema: Hash, id: Hash) -> Result<Self, MessageError> {
let message = Self {
action: MessageAction::Delete,
version: MessageVersion::Default,
schema,
id: Some(id),
fields: None,
};
message.validate()?;
Ok(message)
}
pub fn to_cbor(&self) -> Vec<u8> {
serde_cbor::to_vec(&self).unwrap()
}
pub fn is_create(&self) -> bool {
self.action == MessageAction::Create
}
pub fn is_update(&self) -> bool {
self.action == MessageAction::Update
}
pub fn is_delete(&self) -> bool {
self.action == MessageAction::Delete
}
pub fn action(&self) -> &MessageAction {
&self.action
}
pub fn version(&self) -> &MessageVersion {
&self.version
}
pub fn schema(&self) -> &Hash {
&self.schema
}
pub fn id(&self) -> Option<&Hash> {
self.id.as_ref()
}
pub fn fields(&self) -> Option<&MessageFields> {
self.fields.as_ref()
}
pub fn has_id(&self) -> bool {
self.id.is_some()
}
pub fn has_fields(&self) -> bool {
self.fields.is_some()
}
}
impl From<&MessageEncoded> for Message {
fn from(message_encoded: &MessageEncoded) -> Self {
serde_cbor::from_slice(&message_encoded.to_bytes()).unwrap()
}
}
impl Validate for Message {
type Error = MessageError;
fn validate(&self) -> Result<(), Self::Error> {
if !self.is_delete() && (!self.has_fields() || self.fields().unwrap().is_empty()) {
return Err(MessageError::EmptyFields);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use crate::hash::Hash;
use crate::message::MessageEncoded;
use super::{Message, MessageFields, MessageValue};
#[test]
fn message_fields() {
let mut fields = MessageFields::new();
fields
.add("test", MessageValue::Text("Hello, Panda!".to_owned()))
.unwrap();
assert!(fields
.add("test", MessageValue::Text("Huhu".to_owned()))
.is_err());
assert!(fields
.update("imagine", MessageValue::Text("Pandaparty".to_owned()))
.is_err());
}
#[test]
fn encode_and_decode() {
let mut fields = MessageFields::new();
fields
.add("username", MessageValue::Text("bubu".to_owned()))
.unwrap();
fields.add("height", MessageValue::Float(3.5)).unwrap();
fields.add("age", MessageValue::Integer(28)).unwrap();
fields
.add("is_admin", MessageValue::Boolean(false))
.unwrap();
fields
.add(
"profile_picture",
MessageValue::Relation(Hash::new_from_bytes(vec![1, 2, 3]).unwrap()),
)
.unwrap();
let message = Message::new_update(
Hash::new_from_bytes(vec![1, 255, 0]).unwrap(),
Hash::new_from_bytes(vec![62, 128]).unwrap(),
fields,
)
.unwrap();
assert!(message.is_update());
let encoded = MessageEncoded::try_from(&message).unwrap();
let message_restored = Message::try_from(&encoded).unwrap();
assert_eq!(message, message_restored);
}
#[test]
fn field_ordering() {
let mut fields = MessageFields::new();
fields
.add("a", MessageValue::Text("sloth".to_owned()))
.unwrap();
fields
.add("b", MessageValue::Text("penguin".to_owned()))
.unwrap();
let first_message =
Message::new_create(Hash::new_from_bytes(vec![1, 255, 0]).unwrap(), fields).unwrap();
let mut second_fields = MessageFields::new();
second_fields
.add("b", MessageValue::Text("penguin".to_owned()))
.unwrap();
second_fields
.add("a", MessageValue::Text("sloth".to_owned()))
.unwrap();
let second_message = Message::new_create(
Hash::new_from_bytes(vec![1, 255, 0]).unwrap(),
second_fields,
)
.unwrap();
assert_eq!(first_message.to_cbor(), second_message.to_cbor());
}
#[test]
fn field_iteration() {
let mut fields = MessageFields::new();
fields
.add("a", MessageValue::Text("sloth".to_owned()))
.unwrap();
fields
.add("b", MessageValue::Text("penguin".to_owned()))
.unwrap();
let mut field_iterator = fields.iter();
assert_eq!(
field_iterator.next().unwrap().1,
&MessageValue::Text("sloth".to_owned())
);
assert_eq!(
field_iterator.next().unwrap().1,
&MessageValue::Text("penguin".to_owned())
);
}
}