hydro_lang/deploy/maelstrom/
deploy_runtime_maelstrom.rs1#![allow(
7 unused,
8 reason = "unused in trybuild but the __staged version is needed"
9)]
10#![allow(missing_docs, reason = "used internally")]
11
12use std::io::{BufRead, Write};
13
14use futures::{Stream, StreamExt};
15use serde::{Deserialize, Serialize};
16use stageleft::{QuotedWithContext, RuntimeData, q};
17
18use crate::forward_handle::ForwardHandle;
19use crate::live_collections::boundedness::Unbounded;
20use crate::live_collections::keyed_stream::KeyedStream;
21use crate::live_collections::stream::{ExactlyOnce, NoOrder, TotalOrder};
22use crate::location::dynamic::LocationId;
23use crate::location::member_id::TaglessMemberId;
24use crate::location::{Cluster, LocationKey, MembershipEvent};
25use crate::nondet::nondet;
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct MaelstromMessage<T> {
30 pub src: String,
31 pub dest: String,
32 pub body: T,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct InitBody {
38 #[serde(rename = "type")]
39 pub msg_type: String,
40 pub msg_id: u64,
41 pub node_id: String,
42 pub node_ids: Vec<String>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct InitOkBody {
48 #[serde(rename = "type")]
49 pub msg_type: String,
50 pub in_reply_to: u64,
51}
52
53pub struct MaelstromMeta {
56 pub node_id: String,
57 pub node_ids: Vec<String>,
58 stdin_tx: tokio::sync::broadcast::Sender<String>,
59}
60
61impl MaelstromMeta {
62 pub fn subscribe_stdin(&self) -> tokio_stream::wrappers::BroadcastStream<String> {
65 tokio_stream::wrappers::BroadcastStream::new(self.stdin_tx.subscribe())
66 }
67
68 pub fn start_receiving(&self) {
70 let tx_clone = self.stdin_tx.clone();
71
72 std::thread::spawn(move || {
74 let stdin = std::io::stdin();
75 for line in stdin.lock().lines() {
76 match line {
77 Ok(l) => {
78 let _ = tx_clone.send(l);
80 }
81 Err(_) => break,
82 }
83 }
84 });
85 }
86}
87
88pub fn maelstrom_init() -> MaelstromMeta {
92 let stdin = std::io::stdin();
93 let mut stdout = std::io::stdout();
94
95 let msg: MaelstromMessage<InitBody> = loop {
103 let mut line = String::new();
104 let bytes_read = stdin
105 .lock()
106 .read_line(&mut line)
107 .expect("Failed to read init message");
108
109 if bytes_read == 0 {
110 eprintln!("stdin closed before an init message was received; exiting");
116 std::process::exit(0);
117 }
118
119 let parsed: MaelstromMessage<serde_json::Value> =
120 serde_json::from_str(&line).expect("Failed to parse message while waiting for init");
121
122 if parsed.body.get("type").and_then(|t| t.as_str()) == Some("init") {
123 let body: InitBody =
124 serde_json::from_value(parsed.body).expect("Failed to parse init message");
125 break MaelstromMessage {
126 src: parsed.src,
127 dest: parsed.dest,
128 body,
129 };
130 } else {
131 eprintln!(
132 "dropping message received before init (lossy channel): {}",
133 line.trim_end()
134 );
135 }
136 };
137
138 let (stdin_tx, _) = tokio::sync::broadcast::channel::<String>(1024);
140
141 let meta = MaelstromMeta {
142 node_id: msg.body.node_id.clone(),
143 node_ids: msg.body.node_ids.clone(),
144 stdin_tx,
145 };
146
147 let response = MaelstromMessage {
149 src: msg.body.node_id,
150 dest: msg.src,
151 body: InitOkBody {
152 msg_type: "init_ok".to_owned(),
153 in_reply_to: msg.body.msg_id,
154 },
155 };
156
157 let response_json = serde_json::to_string(&response).expect("Failed to serialize init_ok");
158 writeln!(stdout, "{}", response_json).expect("Failed to write init_ok");
159 stdout.flush().expect("Failed to flush stdout");
160
161 meta
162}
163
164pub(super) fn cluster_members<'a>(
168 meta: RuntimeData<&'a MaelstromMeta>,
169 _of_cluster: LocationKey,
170) -> impl QuotedWithContext<'a, &'a [TaglessMemberId], ()> + Clone + 'a {
171 q!({
172 let members: &'static [TaglessMemberId] = Box::leak(
173 meta.node_ids
174 .iter()
175 .map(|id| TaglessMemberId::from_maelstrom_node_id(id.clone()))
176 .collect::<Vec<TaglessMemberId>>()
177 .into_boxed_slice(),
178 );
179 members
180 })
181}
182
183pub(super) fn cluster_self_id<'a>(
185 meta: RuntimeData<&'a MaelstromMeta>,
186) -> impl QuotedWithContext<'a, TaglessMemberId, ()> + Clone + 'a {
187 q!(TaglessMemberId::from_maelstrom_node_id(
188 meta.node_id.clone()
189 ))
190}
191
192pub(super) fn cluster_membership_stream<'a>(
195 _location_id: &LocationId,
196) -> impl QuotedWithContext<'a, Box<dyn Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin>, ()>
197{
198 let meta: RuntimeData<&MaelstromMeta> = RuntimeData::new("__hydro_lang_maelstrom_meta");
199 q!(Box::new(futures::stream::iter(
200 meta.node_ids
201 .iter()
202 .map(|id| (
203 TaglessMemberId::from_maelstrom_node_id(id.clone()),
204 MembershipEvent::Joined
205 ))
206 .collect::<Vec<_>>()
207 ))
208 as Box<
209 dyn futures::Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin,
210 >)
211}
212
213pub(super) fn deploy_maelstrom_m2m(meta: RuntimeData<&MaelstromMeta>) -> (syn::Expr, syn::Expr) {
216 let sink_expr = q!({
218 let node_id = meta.node_id.clone();
219 sinktools::map(
220 move |(dest_id, payload): (TaglessMemberId, bytes::Bytes)| {
221 let msg = serde_json::json!({
222 "src": node_id,
223 "dest": dest_id.get_maelstrom_node_id(),
224 "body": {
225 "type": "hydro_data",
226 "data": payload.to_vec()
227 }
228 });
229 serde_json::to_string(&msg).unwrap() + "\n"
230 },
231 futures::sink::unfold((), |(), line: String| {
232 Box::pin(async move {
233 print!("{}", line);
234 std::io::stdout().flush().ok();
235 Ok::<_, std::io::Error>(())
236 })
237 }),
238 )
239 })
240 .splice_untyped_ctx(&());
241
242 let source_expr = q!({
244 let node_ids: std::collections::HashSet<String> = meta.node_ids.iter().cloned().collect();
245 let lines = meta.subscribe_stdin();
246 futures::StreamExt::filter_map(lines, move |line_result| {
247 let node_ids = node_ids.clone();
248 Box::pin(async move {
249 let line = line_result.ok()?;
250 let mut msg =
251 serde_json::from_str::<MaelstromMessage<serde_json::Value>>(&line).ok()?;
252 if msg
254 .body
255 .get("type")
256 .is_some_and(|t| t.as_str() == Some("hydro_data"))
257 {
258 let deser: Vec<u8> =
259 serde_json::from_value(msg.body.get_mut("data").unwrap().take()).unwrap();
260 Some(Ok::<_, std::io::Error>((
261 TaglessMemberId::from_maelstrom_node_id(msg.src),
262 bytes::BytesMut::from(&deser[..]),
263 )))
264 } else {
265 None
266 }
267 })
268 })
269 })
270 .splice_untyped_ctx(&());
271
272 (sink_expr, source_expr)
273}
274
275pub fn maelstrom_client_source(
281 meta: &MaelstromMeta,
282) -> impl Stream<Item = (String, serde_json::Value)> + Unpin {
283 use std::collections::HashSet;
284
285 let node_ids: HashSet<String> = meta.node_ids.iter().cloned().collect();
286 let lines = meta.subscribe_stdin();
287
288 Box::pin(lines.filter_map(move |line_result| {
289 let node_ids = node_ids.clone();
290 async move {
291 let line = line_result.ok()?;
292 let msg: MaelstromMessage<serde_json::Value> = serde_json::from_str(&line).ok()?;
293 if !node_ids.contains(&msg.src) {
295 Some((msg.src, msg.body))
296 } else {
297 None
298 }
299 }
300 }))
301}
302
303pub fn maelstrom_send_response(node_id: &str, client_id: &str, body: serde_json::Value) {
307 use std::io::Write;
308
309 let msg = MaelstromMessage {
310 src: node_id.to_owned(),
311 dest: client_id.to_owned(),
312 body,
313 };
314
315 let json = serde_json::to_string(&msg).expect("Failed to serialize response");
316 println!("{}", json);
317 std::io::stdout().flush().ok();
318}