diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Cargo.lock | 98 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | Justfile | 3 | ||||
-rw-r--r-- | common/Cargo.toml | 10 | ||||
-rw-r--r-- | common/src/lib.rs | 41 | ||||
-rw-r--r-- | common/src/msg.rs | 50 | ||||
-rw-r--r-- | echo/Cargo.toml | 11 | ||||
-rw-r--r-- | echo/src/handler.rs | 1 | ||||
-rw-r--r-- | echo/src/main.rs | 108 |
10 files changed, 331 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..076ef8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +maelstrom/ +store/ +target/ +.envrc
\ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3451de6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,98 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "echo" +version = "0.1.0" +dependencies = [ + "common", + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.185" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.185" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..359448b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "echo", + "common" +] diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..940098a --- /dev/null +++ b/Justfile @@ -0,0 +1,3 @@ +test-echo: + cargo build --bin echo + maelstrom test -w echo --bin target/debug/echo --node-count 1 --time-limit 10
\ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..3abc652 --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.185", features = ["derive"] } +serde_json = "1.0.105"
\ No newline at end of file diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..d0db5d1 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,41 @@ +use std::io::{Stdout, Write}; + +use msg::{MaelstromBodyOr, Message, MessageHeader}; +use serde::{Deserialize, Serialize}; + +pub mod msg; + +pub trait Handler { + type Body: Serialize + for<'a> Deserialize<'a>; + + fn init(node_id: String, node_ids: Vec<String>, msg_id: usize) -> Self; + fn handle( + &mut self, + header: MessageHeader, + body: Self::Body, + writer: &mut MsgWriter<Self::Body>, + ) -> (); +} + +pub struct MsgWriter<W> { + node_id: String, + writer: W, +} + +impl<W: Write> MsgWriter<W> { + pub fn new(node_id: String, writer: W) -> Self { + Self { node_id, writer } + } + + pub fn write<T: Serialize>(&mut self, dst: String, msg: &T) { + let msg = Message { + header: MessageHeader { + src: self.node_id.clone(), + dst, + }, + body: MaelstromBodyOr::Other { inner: msg }, + }; + serde_json::to_writer(&mut self.writer, &msg).unwrap(); + self.writer.write(&[b'\n']).unwrap(); + } +} diff --git a/common/src/msg.rs b/common/src/msg.rs new file mode 100644 index 0000000..23db171 --- /dev/null +++ b/common/src/msg.rs @@ -0,0 +1,50 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Message<B> { + #[serde(flatten)] + pub header: MessageHeader, + pub body: MaelstromBodyOr<B>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct MessageHeader { + pub src: String, + #[serde(rename = "dest")] + pub dst: String, +} + +impl MessageHeader { + pub fn flip(self) -> Self { + Self { + src: self.dst, + dst: self.src, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum MaelstromBodyOr<B> { + MaelstromBody { + #[serde(flatten)] + inner: MaelstromBody, + }, + Other { + #[serde(flatten)] + inner: B, + }, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum MaelstromBody { + #[serde(rename = "init")] + Init { + node_id: String, + node_ids: Vec<String>, + msg_id: usize, + }, + #[serde(rename = "init_ok")] + InitOk { msg_id: usize, in_reply_to: usize }, +} diff --git a/echo/Cargo.toml b/echo/Cargo.toml new file mode 100644 index 0000000..14e4218 --- /dev/null +++ b/echo/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "echo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.185", features = ["derive"] } +serde_json = "1.0.105" +common = { path = "../common/" }
\ No newline at end of file diff --git a/echo/src/handler.rs b/echo/src/handler.rs new file mode 100644 index 0000000..4cac571 --- /dev/null +++ b/echo/src/handler.rs @@ -0,0 +1 @@ +use crate::msg::{EchoMessage, MaelstromMsgOr, Message, MessageHeader}; diff --git a/echo/src/main.rs b/echo/src/main.rs new file mode 100644 index 0000000..381099c --- /dev/null +++ b/echo/src/main.rs @@ -0,0 +1,108 @@ +use std::io; + +use common::{msg::*, send_msg, Handler}; +use serde::{Deserialize, Serialize}; +use serde_json::Deserializer; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum EchoBody { + #[serde(rename = "echo")] + Echo { msg_id: usize, echo: String }, + + #[serde(rename = "echo_ok")] + EchoOk { + msg_id: usize, + in_reply_to: usize, + echo: String, + }, +} +fn main() { + let mut stdout = io::stdout(); + let deser = Deserializer::from_reader(io::stdin()); + let mut deser = deser.into_iter::<Message<()>>(); + let Some(msg) = deser.next() else { + panic!("stream ended before init message"); + }; + let Ok(msg) = msg else { + panic!("{}", msg.unwrap_err()); + }; + + let (node_id, node_ids, msg_id) = match msg.body { + MaelstromBodyOr::MaelstromBody { + inner: + MaelstromBody::Init { + node_id, + node_ids, + msg_id, + }, + } => (node_id, node_ids, msg_id), + _ => { + panic!("expected init message to be first message"); + } + }; + + send_msg( + &mut stdout, + &Message { + header: msg.header.flip(), + body: MaelstromBodyOr::MaelstromBody::<()> { + inner: MaelstromBody::InitOk { + msg_id: 0, + in_reply_to: msg_id, + }, + }, + }, + ); + + let mut handler = EchoHandler::init(node_id, node_ids, msg_id); + + drop(deser); + + let deser = Deserializer::from_reader(io::stdin()); + for msg in deser.into_iter::<Message<EchoBody>>() { + let msg = msg.unwrap(); + match msg.body { + MaelstromBodyOr::Other { inner } => { + if let Some(out) = handler.handle(msg.header, inner) { + send_msg(&mut stdout, &out); + } + } + _ => todo!(), + }; + } +} + +pub struct EchoHandler { + next_msg_id: usize, +} + +impl Handler for EchoHandler { + type Body = EchoBody; + + fn init(_node_id: String, _node_ids: Vec<String>, _msg_id: usize) -> Self { + EchoHandler { next_msg_id: 1 } + } + + fn handle(&mut self, header: MessageHeader, body: Self::Body) -> Option<Message<EchoBody>> { + match body { + EchoBody::Echo { msg_id, echo } => { + let msg = Message { + header: header.flip(), + body: MaelstromBodyOr::Other { + inner: EchoBody::EchoOk { + msg_id: self.next_msg_id, + in_reply_to: msg_id, + echo, + }, + }, + }; + + self.next_msg_id += 1; + + Some(msg) + } + EchoBody::EchoOk { .. } => None, + } + } +} |