diff --git a/Cargo.lock b/Cargo.lock index 02c4cdf..4cb5376 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,15 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "maelstrom-protocol" +version = "0.0.1" +dependencies = [ + "serde", + "serde_json", + "serde_repr", +] + [[package]] name = "memchr" version = "2.7.2" @@ -473,6 +482,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 728b983..68a8a92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["gg-echo", "gg-uid", "gg-broadcast"] +members = ["maelstrom-protocol", "gg-echo", "gg-uid", "gg-broadcast"] resolver = "2" [workspace.package] diff --git a/maelstrom-protocol/Cargo.toml b/maelstrom-protocol/Cargo.toml new file mode 100644 index 0000000..42ca124 --- /dev/null +++ b/maelstrom-protocol/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "maelstrom-protocol" +edition = "2021" +version.workspace = true +authors.workspace = true + +[dependencies] +serde = { version = "1", default-features = false, features = ["derive"] } +serde_json = { version = "1", default-features = false, features = ["std"] } +serde_repr = "0.1" diff --git a/maelstrom-protocol/src/lib.rs b/maelstrom-protocol/src/lib.rs new file mode 100644 index 0000000..950e422 --- /dev/null +++ b/maelstrom-protocol/src/lib.rs @@ -0,0 +1,92 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +pub type Payload = Map; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Message { + src: String, + dest: String, + body: Body, +} + +#[derive(Debug, 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, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ErrorCode { + Definite(DefiniteError), + Indefinite(IndefiniteError), +} + +#[derive(Debug, Clone, Serialize_repr, Deserialize_repr)] +#[repr(u64)] +pub enum DefiniteError { + NodeNotFound = 2, + NotSupported = 10, + TemporarilyUnavailable = 11, + MalformedRequest = 12, + Abort = 14, + KeyNotFound = 20, + KeyAlreadyExists = 21, + PreconditionFailed = 22, + TxnConflict = 30, +} + +#[derive(Debug, Clone, Serialize_repr, Deserialize_repr)] +#[repr(u64)] +pub enum IndefiniteError { + Timeout = 0, + Crash = 13, +} + +pub fn init_ok(msg_id: u64, in_reply_to: u64) -> Body { + Body { + typ: "init_ok".to_string(), + msg_id, + in_reply_to, + payload: Payload::new(), + } +} + +pub fn error(msg_id: u64, in_reply_to: u64, code: ErrorCode, text: Option<&str>) -> Body { + Body { + typ: "error".to_string(), + msg_id, + in_reply_to, + payload: [ + ("code".to_string(), serde_json::to_value(code).unwrap()), + ("text".to_string(), serde_json::to_value(text).unwrap()), + ] + .into_iter() + .collect(), + } +} + +#[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"); + } +}