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 }
131 eprintln!(
132 "dropping message received before init (lossy channel): {}",
133 line.trim_end()
134 );
135 };
136
137 let (stdin_tx, _) = tokio::sync::broadcast::channel::<String>(1024);
139
140 let meta = MaelstromMeta {
141 node_id: msg.body.node_id.clone(),
142 node_ids: msg.body.node_ids.clone(),
143 stdin_tx,
144 };
145
146 let response = MaelstromMessage {
148 src: msg.body.node_id,
149 dest: msg.src,
150 body: InitOkBody {
151 msg_type: "init_ok".to_owned(),
152 in_reply_to: msg.body.msg_id,
153 },
154 };
155
156 let response_json = serde_json::to_string(&response).expect("Failed to serialize init_ok");
157 writeln!(stdout, "{}", response_json).expect("Failed to write init_ok");
158 stdout.flush().expect("Failed to flush stdout");
159
160 meta
161}
162
163pub(super) fn cluster_members<'a>(
167 meta: RuntimeData<&'a MaelstromMeta>,
168 _of_cluster: LocationKey,
169) -> impl QuotedWithContext<'a, &'a [TaglessMemberId], ()> + Clone + 'a {
170 q!({
171 let members: &'static [TaglessMemberId] = Box::leak(
172 meta.node_ids
173 .iter()
174 .map(|id| TaglessMemberId::from_maelstrom_node_id(id.clone()))
175 .collect::<Vec<TaglessMemberId>>()
176 .into_boxed_slice(),
177 );
178 members
179 })
180}
181
182pub(super) fn cluster_self_id<'a>(
184 meta: RuntimeData<&'a MaelstromMeta>,
185) -> impl QuotedWithContext<'a, TaglessMemberId, ()> + Clone + 'a {
186 q!(TaglessMemberId::from_maelstrom_node_id(
187 meta.node_id.clone()
188 ))
189}
190
191pub(super) fn cluster_membership_stream<'a>(
194 _location_id: &LocationId,
195) -> impl QuotedWithContext<'a, Box<dyn Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin>, ()>
196{
197 let meta: RuntimeData<&MaelstromMeta> = RuntimeData::new("__hydro_lang_maelstrom_meta");
198 q!(Box::new(futures::stream::iter(
199 meta.node_ids
200 .iter()
201 .map(|id| (
202 TaglessMemberId::from_maelstrom_node_id(id.clone()),
203 MembershipEvent::Joined
204 ))
205 .collect::<Vec<_>>()
206 ))
207 as Box<
208 dyn futures::Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin,
209 >)
210}
211
212pub(super) fn deploy_maelstrom_m2m(meta: RuntimeData<&MaelstromMeta>) -> (syn::Expr, syn::Expr) {
215 let sink_expr = q!({
217 let node_id = meta.node_id.clone();
218 sinktools::map(
219 move |(dest_id, payload): (TaglessMemberId, bytes::Bytes)| {
220 let msg = serde_json::json!({
221 "src": node_id,
222 "dest": dest_id.get_maelstrom_node_id(),
223 "body": {
224 "type": "hydro_data",
225 "data": payload.to_vec()
226 }
227 });
228 let mut line = serde_json::to_string(&msg).unwrap();
229 line.push('\n');
230 line
231 },
232 futures::sink::unfold((), |(), line: String| {
233 Box::pin(async move {
234 print!("{}", line);
235 std::io::stdout().flush().ok();
236 Ok::<_, std::io::Error>(())
237 })
238 }),
239 )
240 })
241 .splice_untyped_ctx(&());
242
243 let source_expr = q!({
245 let node_ids: std::collections::HashSet<String> = meta.node_ids.iter().cloned().collect();
246 let lines = meta.subscribe_stdin();
247 futures::StreamExt::filter_map(lines, move |line_result| {
248 let node_ids = node_ids.clone();
249 Box::pin(async move {
250 let line = line_result.ok()?;
251 let mut msg =
252 serde_json::from_str::<MaelstromMessage<serde_json::Value>>(&line).ok()?;
253 if msg
255 .body
256 .get("type")
257 .is_some_and(|t| t.as_str() == Some("hydro_data"))
258 {
259 let deser: Vec<u8> =
260 serde_json::from_value(msg.body.get_mut("data").unwrap().take()).unwrap();
261 Some(Ok::<_, std::io::Error>((
262 TaglessMemberId::from_maelstrom_node_id(msg.src),
263 bytes::BytesMut::from(&deser[..]),
264 )))
265 } else {
266 None
267 }
268 })
269 })
270 })
271 .splice_untyped_ctx(&());
272
273 (sink_expr, source_expr)
274}
275
276pub fn maelstrom_client_source(
282 meta: &MaelstromMeta,
283) -> impl Stream<Item = (String, serde_json::Value)> + Unpin {
284 use std::collections::HashSet;
285
286 let node_ids: HashSet<String> = meta.node_ids.iter().cloned().collect();
287 let lines = meta.subscribe_stdin();
288
289 Box::pin(lines.filter_map(move |line_result| {
290 let node_ids = node_ids.clone();
291 async move {
292 let line = line_result.ok()?;
293 let msg: MaelstromMessage<serde_json::Value> = serde_json::from_str(&line).ok()?;
294 if !node_ids.contains(&msg.src) {
296 Some((msg.src, msg.body))
297 } else {
298 None
299 }
300 }
301 }))
302}
303
304pub fn maelstrom_send_response(node_id: &str, client_id: &str, body: serde_json::Value) {
308 use std::io::Write;
309
310 let msg = MaelstromMessage {
311 src: node_id.to_owned(),
312 dest: client_id.to_owned(),
313 body,
314 };
315
316 let json = serde_json::to_string(&msg).expect("Failed to serialize response");
317 println!("{}", json);
318 std::io::stdout().flush().ok();
319}