1use rivet_envoy_protocol as protocol;
2use std::collections::HashMap;
3
4use crate::utils::id_to_str;
5
6fn stringify_bytes(data: &[u8]) -> String {
7 format!("Bytes({})", data.len())
8}
9
10fn stringify_map(map: &HashMap<String, String>) -> String {
11 let entries: Vec<String> = map
12 .iter()
13 .map(|(k, v)| format!("\"{k}\": \"{v}\""))
14 .collect();
15 format!("Map({}){{{}}}", map.len(), entries.join(", "))
16}
17
18fn stringify_message_id(msg_id: &protocol::MessageId) -> String {
19 format!(
20 "MessageId{{gatewayId: {}, requestId: {}, messageIndex: {}}}",
21 id_to_str(&msg_id.gateway_id),
22 id_to_str(&msg_id.request_id),
23 msg_id.message_index
24 )
25}
26
27pub fn stringify_to_rivet_tunnel_message_kind(kind: &protocol::ToRivetTunnelMessageKind) -> String {
28 match kind {
29 protocol::ToRivetTunnelMessageKind::ToRivetResponseStart(val) => {
30 let body_str = match &val.body {
31 Some(b) => stringify_bytes(b),
32 None => "null".to_string(),
33 };
34 format!(
35 "ToRivetResponseStart{{status: {}, headers: {}, body: {}, stream: {}}}",
36 val.status,
37 stringify_map(&val.headers),
38 body_str,
39 val.stream
40 )
41 }
42 protocol::ToRivetTunnelMessageKind::ToRivetResponseChunk(val) => {
43 format!(
44 "ToRivetResponseChunk{{body: {}, finish: {}}}",
45 stringify_bytes(&val.body),
46 val.finish
47 )
48 }
49 protocol::ToRivetTunnelMessageKind::ToRivetResponseAbort => {
50 "ToRivetResponseAbort".to_string()
51 }
52 protocol::ToRivetTunnelMessageKind::ToRivetWebSocketOpen(val) => {
53 format!(
54 "ToRivetWebSocketOpen{{canHibernate: {}}}",
55 val.can_hibernate
56 )
57 }
58 protocol::ToRivetTunnelMessageKind::ToRivetWebSocketMessage(val) => {
59 format!(
60 "ToRivetWebSocketMessage{{data: {}, binary: {}}}",
61 stringify_bytes(&val.data),
62 val.binary
63 )
64 }
65 protocol::ToRivetTunnelMessageKind::ToRivetWebSocketMessageAck(val) => {
66 format!("ToRivetWebSocketMessageAck{{index: {}}}", val.index)
67 }
68 protocol::ToRivetTunnelMessageKind::ToRivetWebSocketClose(val) => {
69 let code_str = match &val.code {
70 Some(c) => c.to_string(),
71 None => "null".to_string(),
72 };
73 let reason_str = match &val.reason {
74 Some(r) => format!("\"{r}\""),
75 None => "null".to_string(),
76 };
77 format!(
78 "ToRivetWebSocketClose{{code: {code_str}, reason: {reason_str}, hibernate: {}}}",
79 val.hibernate
80 )
81 }
82 }
83}
84
85pub fn stringify_to_envoy_tunnel_message_kind(kind: &protocol::ToEnvoyTunnelMessageKind) -> String {
86 match kind {
87 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestStart(val) => {
88 let body_str = match &val.body {
89 Some(b) => stringify_bytes(b),
90 None => "null".to_string(),
91 };
92 format!(
93 "ToEnvoyRequestStart{{actorId: \"{}\", method: \"{}\", path: \"{}\", headers: {}, body: {}, stream: {}}}",
94 val.actor_id,
95 val.method,
96 val.path,
97 stringify_map(&val.headers),
98 body_str,
99 val.stream
100 )
101 }
102 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestChunk(val) => {
103 format!(
104 "ToEnvoyRequestChunk{{body: {}, finish: {}}}",
105 stringify_bytes(&val.body),
106 val.finish
107 )
108 }
109 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestAbort => {
110 "ToEnvoyRequestAbort".to_string()
111 }
112 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketOpen(val) => {
113 format!(
114 "ToEnvoyWebSocketOpen{{actorId: \"{}\", path: \"{}\", headers: {}}}",
115 val.actor_id,
116 val.path,
117 stringify_map(&val.headers)
118 )
119 }
120 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketMessage(val) => {
121 format!(
122 "ToEnvoyWebSocketMessage{{data: {}, binary: {}}}",
123 stringify_bytes(&val.data),
124 val.binary
125 )
126 }
127 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketClose(val) => {
128 let code_str = match &val.code {
129 Some(c) => c.to_string(),
130 None => "null".to_string(),
131 };
132 let reason_str = match &val.reason {
133 Some(r) => format!("\"{r}\""),
134 None => "null".to_string(),
135 };
136 format!("ToEnvoyWebSocketClose{{code: {code_str}, reason: {reason_str}}}")
137 }
138 }
139}
140
141pub fn stringify_command(command: &protocol::Command) -> String {
142 match command {
143 protocol::Command::CommandStartActor(val) => {
144 let key_str = match &val.config.key {
145 Some(k) => format!("\"{k}\""),
146 None => "null".to_string(),
147 };
148 let input_str = match &val.config.input {
149 Some(i) => stringify_bytes(i),
150 None => "null".to_string(),
151 };
152 let hib_str = if val.hibernating_requests.is_empty() {
153 "[]".to_string()
154 } else {
155 let entries: Vec<String> = val
156 .hibernating_requests
157 .iter()
158 .map(|hr| {
159 format!(
160 "{{gatewayId: {}, requestId: {}}}",
161 id_to_str(&hr.gateway_id),
162 id_to_str(&hr.request_id)
163 )
164 })
165 .collect();
166 format!("[{}]", entries.join(", "))
167 };
168 format!(
169 "CommandStartActor{{config: {{name: \"{}\", key: {key_str}, createTs: {}, input: {input_str}}}, hibernatingRequests: {hib_str}}}",
170 val.config.name, val.config.create_ts
171 )
172 }
173 protocol::Command::CommandStopActor(val) => {
174 format!("CommandStopActor{{reason: {:?}}}", val.reason)
175 }
176 }
177}
178
179pub fn stringify_command_wrapper(wrapper: &protocol::CommandWrapper) -> String {
180 format!(
181 "CommandWrapper{{actorId: \"{}\", generation: {}, index: {}, inner: {}}}",
182 wrapper.checkpoint.actor_id,
183 wrapper.checkpoint.generation,
184 wrapper.checkpoint.index,
185 stringify_command(&wrapper.inner)
186 )
187}
188
189pub fn stringify_event(event: &protocol::Event) -> String {
190 match event {
191 protocol::Event::EventActorIntent(val) => {
192 let intent_str = match &val.intent {
193 protocol::ActorIntent::ActorIntentSleep => "Sleep",
194 protocol::ActorIntent::ActorIntentStop => "Stop",
195 };
196 format!("EventActorIntent{{intent: {intent_str}}}")
197 }
198 protocol::Event::EventActorStateUpdate(val) => {
199 let state_str = match &val.state {
200 protocol::ActorState::ActorStateRunning => "Running".to_string(),
201 protocol::ActorState::ActorStateStopped(stopped) => {
202 let message_str = match &stopped.message {
203 Some(m) => format!("\"{m}\""),
204 None => "null".to_string(),
205 };
206 format!(
207 "Stopped{{code: {:?}, message: {message_str}}}",
208 stopped.code
209 )
210 }
211 };
212 format!("EventActorStateUpdate{{state: {state_str}}}")
213 }
214 protocol::Event::EventActorSetAlarm(val) => {
215 let alarm_str = match val.alarm_ts {
216 Some(ts) => ts.to_string(),
217 None => "null".to_string(),
218 };
219 format!("EventActorSetAlarm{{alarmTs: {alarm_str}}}")
220 }
221 }
222}
223
224pub fn stringify_event_wrapper(wrapper: &protocol::EventWrapper) -> String {
225 format!(
226 "EventWrapper{{actorId: {}, generation: {}, index: {}, inner: {}}}",
227 wrapper.checkpoint.actor_id,
228 wrapper.checkpoint.generation,
229 wrapper.checkpoint.index,
230 stringify_event(&wrapper.inner)
231 )
232}
233
234pub fn stringify_to_rivet(message: &protocol::ToRivet) -> String {
235 match message {
236 protocol::ToRivet::ToRivetMetadata(_) => "ToRivetMetadata".to_string(),
237 protocol::ToRivet::ToRivetEvents(events) => {
238 let event_strs: Vec<String> = events.iter().map(stringify_event_wrapper).collect();
239 format!(
240 "ToRivetEvents{{count: {}, events: [{}]}}",
241 events.len(),
242 event_strs.join(", ")
243 )
244 }
245 protocol::ToRivet::ToRivetAckCommands(val) => {
246 let checkpoints: Vec<String> = val
247 .last_command_checkpoints
248 .iter()
249 .map(|cp| format!("{{actorId: \"{}\", index: {}}}", cp.actor_id, cp.index))
250 .collect();
251 format!(
252 "ToRivetAckCommands{{lastCommandCheckpoints: [{}]}}",
253 checkpoints.join(", ")
254 )
255 }
256 protocol::ToRivet::ToRivetStopping => "ToRivetStopping".to_string(),
257 protocol::ToRivet::ToRivetPong(val) => {
258 format!("ToRivetPong{{ts: {}}}", val.ts)
259 }
260 protocol::ToRivet::ToRivetKvRequest(val) => {
261 format!(
262 "ToRivetKvRequest{{actorId: \"{}\", requestId: {}}}",
263 val.actor_id, val.request_id
264 )
265 }
266 protocol::ToRivet::ToRivetSqliteGetPagesRequest(val) => {
267 format!(
268 "ToRivetSqliteGetPagesRequest{{requestId: {}}}",
269 val.request_id
270 )
271 }
272 protocol::ToRivet::ToRivetSqliteCommitRequest(val) => {
273 format!(
274 "ToRivetSqliteCommitRequest{{requestId: {}}}",
275 val.request_id
276 )
277 }
278 protocol::ToRivet::ToRivetSqliteExecRequest(val) => {
279 format!(
280 "ToRivetSqliteExecRequest{{requestId: {}, actorId: \"{}\", generation: {}}}",
281 val.request_id, val.data.actor_id, val.data.generation
282 )
283 }
284 protocol::ToRivet::ToRivetSqliteExecuteRequest(val) => {
285 format!(
286 "ToRivetSqliteExecuteRequest{{requestId: {}, actorId: \"{}\", generation: {}}}",
287 val.request_id, val.data.actor_id, val.data.generation
288 )
289 }
290 protocol::ToRivet::ToRivetSqliteExecuteBatchRequest(val) => {
291 format!(
292 "ToRivetSqliteExecuteBatchRequest{{requestId: {}, actorId: \"{}\", generation: {}, statements: {}}}",
293 val.request_id,
294 val.data.actor_id,
295 val.data.generation,
296 val.data.statements.len()
297 )
298 }
299 protocol::ToRivet::ToRivetTunnelMessage(val) => {
300 format!(
301 "ToRivetTunnelMessage{{messageId: {}, messageKind: {}}}",
302 stringify_message_id(&val.message_id),
303 stringify_to_rivet_tunnel_message_kind(&val.message_kind)
304 )
305 }
306 }
307}
308
309pub fn stringify_to_envoy(message: &protocol::ToEnvoy) -> String {
310 match message {
311 protocol::ToEnvoy::ToEnvoyInit(val) => {
312 format!(
313 "ToEnvoyInit{{metadata: {{envoyLostThreshold: {}, actorStopThreshold: {}}}}}",
314 val.metadata.envoy_lost_threshold, val.metadata.actor_stop_threshold
315 )
316 }
317 protocol::ToEnvoy::ToEnvoyCommands(commands) => {
318 let cmd_strs: Vec<String> = commands.iter().map(stringify_command_wrapper).collect();
319 format!(
320 "ToEnvoyCommands{{count: {}, commands: [{}]}}",
321 commands.len(),
322 cmd_strs.join(", ")
323 )
324 }
325 protocol::ToEnvoy::ToEnvoyAckEvents(val) => {
326 let checkpoints: Vec<String> = val
327 .last_event_checkpoints
328 .iter()
329 .map(|cp| format!("{{actorId: \"{}\", index: {}}}", cp.actor_id, cp.index))
330 .collect();
331 format!(
332 "ToEnvoyAckEvents{{lastEventCheckpoints: [{}]}}",
333 checkpoints.join(", ")
334 )
335 }
336 protocol::ToEnvoy::ToEnvoyKvResponse(val) => {
337 format!("ToEnvoyKvResponse{{requestId: {}}}", val.request_id)
338 }
339 protocol::ToEnvoy::ToEnvoySqliteGetPagesResponse(val) => {
340 format!(
341 "ToEnvoySqliteGetPagesResponse{{requestId: {}}}",
342 val.request_id
343 )
344 }
345 protocol::ToEnvoy::ToEnvoySqliteCommitResponse(val) => {
346 format!(
347 "ToEnvoySqliteCommitResponse{{requestId: {}}}",
348 val.request_id
349 )
350 }
351 protocol::ToEnvoy::ToEnvoySqliteExecResponse(val) => {
352 format!("ToEnvoySqliteExecResponse{{requestId: {}}}", val.request_id)
353 }
354 protocol::ToEnvoy::ToEnvoySqliteExecuteResponse(val) => {
355 format!(
356 "ToEnvoySqliteExecuteResponse{{requestId: {}}}",
357 val.request_id
358 )
359 }
360 protocol::ToEnvoy::ToEnvoySqliteExecuteBatchResponse(val) => {
361 format!(
362 "ToEnvoySqliteExecuteBatchResponse{{requestId: {}}}",
363 val.request_id
364 )
365 }
366 protocol::ToEnvoy::ToEnvoyTunnelMessage(val) => {
367 format!(
368 "ToEnvoyTunnelMessage{{messageId: {}, messageKind: {}}}",
369 stringify_message_id(&val.message_id),
370 stringify_to_envoy_tunnel_message_kind(&val.message_kind)
371 )
372 }
373 protocol::ToEnvoy::ToEnvoyPing(val) => {
374 format!("ToEnvoyPing{{ts: {}}}", val.ts)
375 }
376 }
377}