nebkor-maelstrom/src/protocol.rs

168 lines
4.0 KiB
Rust

use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use serde_repr::{Deserialize_repr, Serialize_repr};
pub type Payload = Map<String, Value>;
#[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<ErrorCode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
}
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, text: Option<&str>) -> Self {
Body {
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);
}
}