summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Cargo.lock98
-rw-r--r--Cargo.toml5
-rw-r--r--Justfile3
-rw-r--r--common/Cargo.toml10
-rw-r--r--common/src/lib.rs41
-rw-r--r--common/src/msg.rs50
-rw-r--r--echo/Cargo.toml11
-rw-r--r--echo/src/handler.rs1
-rw-r--r--echo/src/main.rs108
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,
+ }
+ }
+}