use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use serde_repr::{Deserialize_repr, Serialize_repr}; pub type Payload = Map; #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Message { pub src: String, pub dest: String, pub body: Body, } impl Message { /// `src` and `dest` will be empty. pub fn from_body(body: Body) -> Self { Message { body, ..Default::default() } } pub fn with_body(self, body: Body) -> Self { let mut m = self; m.body = body; m } pub fn from_dest(dest: &str) -> Self { Message { dest: dest.to_string(), ..Default::default() } } pub fn with_dest(self, dest: &str) -> Self { let mut m = self; m.dest = dest.to_string(); m } pub fn from_src(src: &str) -> Self { Message { src: src.to_string(), ..Default::default() } } pub fn with_src(self, src: &str) -> Self { let mut m = self; m.src = src.to_string(); m } /// The Maelstrom type of a Body (and hence a Message) is just a string. This is for the sake of /// ease of use for doing the Gossip Glomers challenges; this crate is not meant to be a real /// network client framework. pub fn typ(&self) -> &str { self.body.typ.as_str() } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Body { #[serde(rename = "type")] pub typ: String, #[serde(default, skip_serializing_if = "u64_zero_by_ref")] pub msg_id: u64, #[serde(default, skip_serializing_if = "u64_zero_by_ref")] pub in_reply_to: u64, #[serde(flatten)] pub payload: Payload, // the following are for the case of errors #[serde(default, skip_serializing_if = "Option::is_none")] pub code: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub text: Option, } impl Body { pub fn from_type(typ: &str) -> Self { Body { typ: typ.to_string(), ..Default::default() } } pub fn with_msg_id(self, msg_id: u64) -> Self { let mut b = self; b.msg_id = msg_id; b } pub fn with_in_reply_to(self, in_reply_to: u64) -> Self { let mut b = self; b.in_reply_to = in_reply_to; b } pub fn with_payload(self, payload: Payload) -> Self { let mut b = self; b.payload = payload; b } pub fn error(code: ErrorCode, in_reply_to: u64, text: Option<&str>) -> Self { Body { in_reply_to, typ: "error".to_string(), code: Some(code), text: text.map(|t| t.to_string()), ..Default::default() } } } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(untagged)] pub enum ErrorCode { Definite(DefiniteError), Indefinite(IndefiniteError), } #[derive( Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, PartialOrd, Ord, Hash, )] #[repr(u16)] pub enum DefiniteError { NodeNotFound = 2, NotSupported = 10, TemporarilyUnavailable = 11, MalformedRequest = 12, Abort = 14, KeyNotFound = 20, KeyAlreadyExists = 21, PreconditionFailed = 22, TxnConflict = 30, } #[derive( Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, PartialOrd, Ord, Hash, )] #[repr(u16)] pub enum IndefiniteError { Timeout = 0, Crash = 13, } #[allow(clippy::trivially_copy_pass_by_ref)] fn u64_zero_by_ref(num: &u64) -> bool { *num == 0 } #[cfg(test)] mod test { use super::*; #[test] fn error_codes() { let ec = ErrorCode::Definite(DefiniteError::Abort); let e = serde_json::to_string(&ec).unwrap(); assert_eq!(&e, "14"); let s: ErrorCode = serde_json::from_str(&e).unwrap(); assert_eq!(s, ec); let n: ErrorCode = serde_json::from_value(14u16.into()).unwrap(); assert_eq!(n, ec); } }