1use anyhow::{Result, bail};
2use serde_bare::Uint;
3use vbare::OwnedVersionedData;
4
5use crate::generated::{v1, v2, v3, v4, v5};
6
7const WORKFLOW_HISTORY_DROPPED_ERROR: &str = "inspector.workflow_history_dropped";
8const QUEUE_DROPPED_ERROR: &str = "inspector.queue_dropped";
9const TRACE_DROPPED_ERROR: &str = "inspector.trace_dropped";
10const DATABASE_DROPPED_ERROR: &str = "inspector.database_dropped";
11
12pub enum ToServer {
13 V1(v1::ToServer),
14 V2(v2::ToServer),
15 V3(v3::ToServer),
16 V4(v4::ToServer),
17 V5(v5::ToServer),
18}
19
20impl OwnedVersionedData for ToServer {
21 type Latest = v5::ToServer;
22
23 fn wrap_latest(latest: Self::Latest) -> Self {
24 Self::V5(latest)
25 }
26
27 fn unwrap_latest(self) -> Result<Self::Latest> {
28 match self {
29 Self::V5(data) => Ok(data),
30 _ => bail!("version not latest"),
31 }
32 }
33
34 fn deserialize_version(payload: &[u8], version: u16) -> Result<Self> {
35 match version {
36 1 => Ok(Self::V1(serde_bare::from_slice(payload)?)),
37 2 => Ok(Self::V2(serde_bare::from_slice(payload)?)),
38 3 => Ok(Self::V3(serde_bare::from_slice(payload)?)),
39 4 => Ok(Self::V4(serde_bare::from_slice(payload)?)),
40 5 => Ok(Self::V5(serde_bare::from_slice(payload)?)),
41 _ => bail!("invalid inspector protocol version for ToServer: {version}"),
42 }
43 }
44
45 fn serialize_version(self, version: u16) -> Result<Vec<u8>> {
46 match (self, version) {
47 (Self::V1(data), 1) => serde_bare::to_vec(&data).map_err(Into::into),
48 (Self::V2(data), 2) => serde_bare::to_vec(&data).map_err(Into::into),
49 (Self::V3(data), 3) => serde_bare::to_vec(&data).map_err(Into::into),
50 (Self::V4(data), 4) => serde_bare::to_vec(&data).map_err(Into::into),
51 (Self::V5(data), 5) => serde_bare::to_vec(&data).map_err(Into::into),
52 (_, version) => bail!("unexpected inspector protocol version for ToServer: {version}"),
53 }
54 }
55
56 fn deserialize_converters() -> Vec<impl Fn(Self) -> Result<Self>> {
57 vec![
58 Self::v1_to_v2,
59 Self::v2_to_v3,
60 Self::v3_to_v4,
61 Self::v4_to_v5,
62 ]
63 }
64
65 fn serialize_converters() -> Vec<impl Fn(Self) -> Result<Self>> {
66 vec![
67 Self::v5_to_v4,
68 Self::v4_to_v3,
69 Self::v3_to_v2,
70 Self::v2_to_v1,
71 ]
72 }
73}
74
75impl ToServer {
76 fn v1_to_v2(self) -> Result<Self> {
77 let Self::V1(data) = self else {
78 bail!("expected inspector protocol v1 ToServer")
79 };
80
81 let body = match data.body {
82 v1::ToServerBody::PatchStateRequest(req) => {
83 v2::ToServerBody::PatchStateRequest(req.into())
84 }
85 v1::ToServerBody::StateRequest(req) => v2::ToServerBody::StateRequest(req.into()),
86 v1::ToServerBody::ConnectionsRequest(req) => {
87 v2::ToServerBody::ConnectionsRequest(req.into())
88 }
89 v1::ToServerBody::ActionRequest(req) => v2::ToServerBody::ActionRequest(req.into()),
90 v1::ToServerBody::RpcsListRequest(req) => v2::ToServerBody::RpcsListRequest(req.into()),
91 v1::ToServerBody::EventsRequest(_) | v1::ToServerBody::ClearEventsRequest(_) => {
92 bail!("cannot convert inspector v1 events requests to v2")
93 }
94 };
95
96 Ok(Self::V2(v2::ToServer { body }))
97 }
98
99 fn v2_to_v3(self) -> Result<Self> {
100 let Self::V2(data) = self else {
101 bail!("expected inspector protocol v2 ToServer")
102 };
103 Ok(Self::V3(data.into()))
104 }
105
106 fn v3_to_v4(self) -> Result<Self> {
107 let Self::V3(data) = self else {
108 bail!("expected inspector protocol v3 ToServer")
109 };
110
111 let body = match data.body {
112 v3::ToServerBody::PatchStateRequest(req) => {
113 v4::ToServerBody::PatchStateRequest(req.into())
114 }
115 v3::ToServerBody::StateRequest(req) => v4::ToServerBody::StateRequest(req.into()),
116 v3::ToServerBody::ConnectionsRequest(req) => {
117 v4::ToServerBody::ConnectionsRequest(req.into())
118 }
119 v3::ToServerBody::ActionRequest(req) => v4::ToServerBody::ActionRequest(req.into()),
120 v3::ToServerBody::RpcsListRequest(req) => v4::ToServerBody::RpcsListRequest(req.into()),
121 v3::ToServerBody::TraceQueryRequest(req) => {
122 v4::ToServerBody::TraceQueryRequest(req.into())
123 }
124 v3::ToServerBody::QueueRequest(req) => v4::ToServerBody::QueueRequest(req.into()),
125 v3::ToServerBody::WorkflowHistoryRequest(req) => {
126 v4::ToServerBody::WorkflowHistoryRequest(req.into())
127 }
128 v3::ToServerBody::DatabaseSchemaRequest(req) => {
129 v4::ToServerBody::DatabaseSchemaRequest(req.into())
130 }
131 v3::ToServerBody::DatabaseTableRowsRequest(req) => {
132 v4::ToServerBody::DatabaseTableRowsRequest(req.into())
133 }
134 };
135
136 Ok(Self::V4(v4::ToServer { body }))
137 }
138
139 fn v4_to_v5(self) -> Result<Self> {
140 let Self::V4(data) = self else {
141 bail!("expected inspector protocol v4 ToServer")
142 };
143 Ok(Self::V5(data.into()))
144 }
145
146 fn v5_to_v4(self) -> Result<Self> {
147 let Self::V5(data) = self else {
148 bail!("expected inspector protocol v5 ToServer")
149 };
150 Ok(Self::V4(data.into()))
151 }
152
153 fn v4_to_v3(self) -> Result<Self> {
154 let Self::V4(data) = self else {
155 bail!("expected inspector protocol v4 ToServer")
156 };
157
158 let body = match data.body {
159 v4::ToServerBody::PatchStateRequest(req) => {
160 v3::ToServerBody::PatchStateRequest(req.into())
161 }
162 v4::ToServerBody::StateRequest(req) => v3::ToServerBody::StateRequest(req.into()),
163 v4::ToServerBody::ConnectionsRequest(req) => {
164 v3::ToServerBody::ConnectionsRequest(req.into())
165 }
166 v4::ToServerBody::ActionRequest(req) => v3::ToServerBody::ActionRequest(req.into()),
167 v4::ToServerBody::RpcsListRequest(req) => v3::ToServerBody::RpcsListRequest(req.into()),
168 v4::ToServerBody::TraceQueryRequest(req) => {
169 v3::ToServerBody::TraceQueryRequest(req.into())
170 }
171 v4::ToServerBody::QueueRequest(req) => v3::ToServerBody::QueueRequest(req.into()),
172 v4::ToServerBody::WorkflowHistoryRequest(req) => {
173 v3::ToServerBody::WorkflowHistoryRequest(req.into())
174 }
175 v4::ToServerBody::WorkflowReplayRequest(_) => {
176 bail!("cannot convert inspector v4 workflow replay requests to v3")
177 }
178 v4::ToServerBody::DatabaseSchemaRequest(req) => {
179 v3::ToServerBody::DatabaseSchemaRequest(req.into())
180 }
181 v4::ToServerBody::DatabaseTableRowsRequest(req) => {
182 v3::ToServerBody::DatabaseTableRowsRequest(req.into())
183 }
184 };
185
186 Ok(Self::V3(v3::ToServer { body }))
187 }
188
189 fn v3_to_v2(self) -> Result<Self> {
190 let Self::V3(data) = self else {
191 bail!("expected inspector protocol v3 ToServer")
192 };
193
194 let body = match data.body {
195 v3::ToServerBody::PatchStateRequest(req) => {
196 v2::ToServerBody::PatchStateRequest(req.into())
197 }
198 v3::ToServerBody::StateRequest(req) => v2::ToServerBody::StateRequest(req.into()),
199 v3::ToServerBody::ConnectionsRequest(req) => {
200 v2::ToServerBody::ConnectionsRequest(req.into())
201 }
202 v3::ToServerBody::ActionRequest(req) => v2::ToServerBody::ActionRequest(req.into()),
203 v3::ToServerBody::RpcsListRequest(req) => v2::ToServerBody::RpcsListRequest(req.into()),
204 v3::ToServerBody::TraceQueryRequest(req) => {
205 v2::ToServerBody::TraceQueryRequest(req.into())
206 }
207 v3::ToServerBody::QueueRequest(req) => v2::ToServerBody::QueueRequest(req.into()),
208 v3::ToServerBody::WorkflowHistoryRequest(req) => {
209 v2::ToServerBody::WorkflowHistoryRequest(req.into())
210 }
211 v3::ToServerBody::DatabaseSchemaRequest(_)
212 | v3::ToServerBody::DatabaseTableRowsRequest(_) => {
213 bail!("cannot convert inspector v3 database requests to v2")
214 }
215 };
216
217 Ok(Self::V2(v2::ToServer { body }))
218 }
219
220 fn v2_to_v1(self) -> Result<Self> {
221 let Self::V2(data) = self else {
222 bail!("expected inspector protocol v2 ToServer")
223 };
224
225 let body = match data.body {
226 v2::ToServerBody::PatchStateRequest(req) => {
227 v1::ToServerBody::PatchStateRequest(req.into())
228 }
229 v2::ToServerBody::StateRequest(req) => v1::ToServerBody::StateRequest(req.into()),
230 v2::ToServerBody::ConnectionsRequest(req) => {
231 v1::ToServerBody::ConnectionsRequest(req.into())
232 }
233 v2::ToServerBody::ActionRequest(req) => v1::ToServerBody::ActionRequest(req.into()),
234 v2::ToServerBody::RpcsListRequest(req) => v1::ToServerBody::RpcsListRequest(req.into()),
235 v2::ToServerBody::TraceQueryRequest(_)
236 | v2::ToServerBody::QueueRequest(_)
237 | v2::ToServerBody::WorkflowHistoryRequest(_) => {
238 bail!("cannot convert inspector v2 queue/trace/workflow requests to v1")
239 }
240 };
241
242 Ok(Self::V1(v1::ToServer { body }))
243 }
244}
245
246pub enum ToClient {
247 V1(v1::ToClient),
248 V2(v2::ToClient),
249 V3(v3::ToClient),
250 V4(v4::ToClient),
251 V5(v5::ToClient),
252}
253
254impl OwnedVersionedData for ToClient {
255 type Latest = v5::ToClient;
256
257 fn wrap_latest(latest: Self::Latest) -> Self {
258 Self::V5(latest)
259 }
260
261 fn unwrap_latest(self) -> Result<Self::Latest> {
262 match self {
263 Self::V5(data) => Ok(data),
264 _ => bail!("version not latest"),
265 }
266 }
267
268 fn deserialize_version(payload: &[u8], version: u16) -> Result<Self> {
269 match version {
270 1 => Ok(Self::V1(serde_bare::from_slice(payload)?)),
271 2 => Ok(Self::V2(serde_bare::from_slice(payload)?)),
272 3 => Ok(Self::V3(serde_bare::from_slice(payload)?)),
273 4 => Ok(Self::V4(serde_bare::from_slice(payload)?)),
274 5 => Ok(Self::V5(serde_bare::from_slice(payload)?)),
275 _ => bail!("invalid inspector protocol version for ToClient: {version}"),
276 }
277 }
278
279 fn serialize_version(self, version: u16) -> Result<Vec<u8>> {
280 match (self, version) {
281 (Self::V1(data), 1) => serde_bare::to_vec(&data).map_err(Into::into),
282 (Self::V2(data), 2) => serde_bare::to_vec(&data).map_err(Into::into),
283 (Self::V3(data), 3) => serde_bare::to_vec(&data).map_err(Into::into),
284 (Self::V4(data), 4) => serde_bare::to_vec(&data).map_err(Into::into),
285 (Self::V5(data), 5) => serde_bare::to_vec(&data).map_err(Into::into),
286 (_, version) => bail!("unexpected inspector protocol version for ToClient: {version}"),
287 }
288 }
289
290 fn deserialize_converters() -> Vec<impl Fn(Self) -> Result<Self>> {
291 vec![
292 Self::v1_to_v2,
293 Self::v2_to_v3,
294 Self::v3_to_v4,
295 Self::v4_to_v5,
296 ]
297 }
298
299 fn serialize_converters() -> Vec<impl Fn(Self) -> Result<Self>> {
300 vec![
301 Self::v5_to_v4,
302 Self::v4_to_v3,
303 Self::v3_to_v2,
304 Self::v2_to_v1,
305 ]
306 }
307}
308
309impl ToClient {
310 fn v1_to_v2(self) -> Result<Self> {
311 let Self::V1(data) = self else {
312 bail!("expected inspector protocol v1 ToClient")
313 };
314
315 let body = match data.body {
316 v1::ToClientBody::StateResponse(resp) => v2::ToClientBody::StateResponse(resp.into()),
317 v1::ToClientBody::ConnectionsResponse(resp) => {
318 v2::ToClientBody::ConnectionsResponse(resp.into())
319 }
320 v1::ToClientBody::ActionResponse(resp) => v2::ToClientBody::ActionResponse(resp.into()),
321 v1::ToClientBody::RpcsListResponse(resp) => {
322 v2::ToClientBody::RpcsListResponse(resp.into())
323 }
324 v1::ToClientBody::ConnectionsUpdated(update) => {
325 v2::ToClientBody::ConnectionsUpdated(update.into())
326 }
327 v1::ToClientBody::StateUpdated(update) => v2::ToClientBody::StateUpdated(update.into()),
328 v1::ToClientBody::Error(error) => v2::ToClientBody::Error(error.into()),
329 v1::ToClientBody::Init(init) => v2::ToClientBody::Init(v2::Init {
330 connections: convert_vec(init.connections),
331 state: init.state,
332 is_state_enabled: init.is_state_enabled,
333 rpcs: init.rpcs,
334 is_database_enabled: init.is_database_enabled,
335 queue_size: Uint(0),
336 workflow_history: None,
337 is_workflow_enabled: false,
338 }),
339 v1::ToClientBody::EventsResponse(_) | v1::ToClientBody::EventsUpdated(_) => {
340 bail!("cannot convert inspector v1 events responses to v2")
341 }
342 };
343
344 Ok(Self::V2(v2::ToClient { body }))
345 }
346
347 fn v2_to_v3(self) -> Result<Self> {
348 let Self::V2(data) = self else {
349 bail!("expected inspector protocol v2 ToClient")
350 };
351 Ok(Self::V3(data.into()))
352 }
353
354 fn v3_to_v4(self) -> Result<Self> {
355 let Self::V3(data) = self else {
356 bail!("expected inspector protocol v3 ToClient")
357 };
358
359 let body = match data.body {
360 v3::ToClientBody::StateResponse(resp) => v4::ToClientBody::StateResponse(resp.into()),
361 v3::ToClientBody::ConnectionsResponse(resp) => {
362 v4::ToClientBody::ConnectionsResponse(resp.into())
363 }
364 v3::ToClientBody::ActionResponse(resp) => v4::ToClientBody::ActionResponse(resp.into()),
365 v3::ToClientBody::ConnectionsUpdated(update) => {
366 v4::ToClientBody::ConnectionsUpdated(update.into())
367 }
368 v3::ToClientBody::QueueUpdated(update) => v4::ToClientBody::QueueUpdated(update.into()),
369 v3::ToClientBody::StateUpdated(update) => v4::ToClientBody::StateUpdated(update.into()),
370 v3::ToClientBody::WorkflowHistoryUpdated(update) => {
371 v4::ToClientBody::WorkflowHistoryUpdated(update.into())
372 }
373 v3::ToClientBody::RpcsListResponse(resp) => {
374 v4::ToClientBody::RpcsListResponse(resp.into())
375 }
376 v3::ToClientBody::TraceQueryResponse(resp) => {
377 v4::ToClientBody::TraceQueryResponse(resp.into())
378 }
379 v3::ToClientBody::QueueResponse(resp) => v4::ToClientBody::QueueResponse(resp.into()),
380 v3::ToClientBody::WorkflowHistoryResponse(resp) => {
381 v4::ToClientBody::WorkflowHistoryResponse(resp.into())
382 }
383 v3::ToClientBody::Error(error) => v4::ToClientBody::Error(error.into()),
384 v3::ToClientBody::Init(init) => v4::ToClientBody::Init(init.into()),
385 v3::ToClientBody::DatabaseSchemaResponse(resp) => {
386 v4::ToClientBody::DatabaseSchemaResponse(resp.into())
387 }
388 v3::ToClientBody::DatabaseTableRowsResponse(resp) => {
389 v4::ToClientBody::DatabaseTableRowsResponse(resp.into())
390 }
391 };
392
393 Ok(Self::V4(v4::ToClient { body }))
394 }
395
396 fn v4_to_v5(self) -> Result<Self> {
397 let Self::V4(data) = self else {
398 bail!("expected inspector protocol v4 ToClient")
399 };
400
401 let body = match data.body {
402 v4::ToClientBody::StateResponse(resp) => v5::ToClientBody::StateResponse(resp.into()),
403 v4::ToClientBody::ConnectionsResponse(resp) => {
404 v5::ToClientBody::ConnectionsResponse(resp.into())
405 }
406 v4::ToClientBody::ActionResponse(resp) => v5::ToClientBody::ActionResponse(resp.into()),
407 v4::ToClientBody::ConnectionsUpdated(update) => {
408 v5::ToClientBody::ConnectionsUpdated(update.into())
409 }
410 v4::ToClientBody::QueueUpdated(update) => v5::ToClientBody::QueueUpdated(update.into()),
411 v4::ToClientBody::StateUpdated(update) => v5::ToClientBody::StateUpdated(update.into()),
412 v4::ToClientBody::WorkflowHistoryUpdated(update) => {
413 v5::ToClientBody::WorkflowHistoryUpdated(update.into())
414 }
415 v4::ToClientBody::RpcsListResponse(resp) => {
416 v5::ToClientBody::RpcsListResponse(resp.into())
417 }
418 v4::ToClientBody::TraceQueryResponse(resp) => {
419 v5::ToClientBody::TraceQueryResponse(resp.into())
420 }
421 v4::ToClientBody::QueueResponse(resp) => v5::ToClientBody::QueueResponse(resp.into()),
422 v4::ToClientBody::WorkflowHistoryResponse(resp) => {
423 v5::ToClientBody::WorkflowHistoryResponse(resp.into())
424 }
425 v4::ToClientBody::WorkflowReplayResponse(resp) => {
426 v5::ToClientBody::WorkflowReplayResponse(resp.into())
427 }
428 v4::ToClientBody::Error(error) => v5::ToClientBody::Error(error.into()),
429 v4::ToClientBody::Init(init) => v5::ToClientBody::Init(v5::Init {
433 connections: convert_vec(init.connections),
434 state: init.state,
435 is_state_enabled: init.is_state_enabled,
436 rpcs: init.rpcs,
437 is_database_enabled: init.is_database_enabled,
438 queue_size: init.queue_size,
439 workflow_history: init.workflow_history,
440 is_workflow_enabled: init.is_workflow_enabled,
441 tab_config: Vec::new(),
442 }),
443 v4::ToClientBody::DatabaseSchemaResponse(resp) => {
444 v5::ToClientBody::DatabaseSchemaResponse(resp.into())
445 }
446 v4::ToClientBody::DatabaseTableRowsResponse(resp) => {
447 v5::ToClientBody::DatabaseTableRowsResponse(resp.into())
448 }
449 };
450
451 Ok(Self::V5(v5::ToClient { body }))
452 }
453
454 fn v5_to_v4(self) -> Result<Self> {
455 let Self::V5(data) = self else {
456 bail!("expected inspector protocol v5 ToClient")
457 };
458
459 let body = match data.body {
460 v5::ToClientBody::StateResponse(resp) => v4::ToClientBody::StateResponse(resp.into()),
461 v5::ToClientBody::ConnectionsResponse(resp) => {
462 v4::ToClientBody::ConnectionsResponse(resp.into())
463 }
464 v5::ToClientBody::ActionResponse(resp) => v4::ToClientBody::ActionResponse(resp.into()),
465 v5::ToClientBody::ConnectionsUpdated(update) => {
466 v4::ToClientBody::ConnectionsUpdated(update.into())
467 }
468 v5::ToClientBody::QueueUpdated(update) => v4::ToClientBody::QueueUpdated(update.into()),
469 v5::ToClientBody::StateUpdated(update) => v4::ToClientBody::StateUpdated(update.into()),
470 v5::ToClientBody::WorkflowHistoryUpdated(update) => {
471 v4::ToClientBody::WorkflowHistoryUpdated(update.into())
472 }
473 v5::ToClientBody::RpcsListResponse(resp) => {
474 v4::ToClientBody::RpcsListResponse(resp.into())
475 }
476 v5::ToClientBody::TraceQueryResponse(resp) => {
477 v4::ToClientBody::TraceQueryResponse(resp.into())
478 }
479 v5::ToClientBody::QueueResponse(resp) => v4::ToClientBody::QueueResponse(resp.into()),
480 v5::ToClientBody::WorkflowHistoryResponse(resp) => {
481 v4::ToClientBody::WorkflowHistoryResponse(resp.into())
482 }
483 v5::ToClientBody::WorkflowReplayResponse(resp) => {
484 v4::ToClientBody::WorkflowReplayResponse(resp.into())
485 }
486 v5::ToClientBody::Error(error) => v4::ToClientBody::Error(error.into()),
487 v5::ToClientBody::Init(init) => v4::ToClientBody::Init(v4::Init {
490 connections: convert_vec(init.connections),
491 state: init.state,
492 is_state_enabled: init.is_state_enabled,
493 rpcs: init.rpcs,
494 is_database_enabled: init.is_database_enabled,
495 queue_size: init.queue_size,
496 workflow_history: init.workflow_history,
497 is_workflow_enabled: init.is_workflow_enabled,
498 }),
499 v5::ToClientBody::DatabaseSchemaResponse(resp) => {
500 v4::ToClientBody::DatabaseSchemaResponse(resp.into())
501 }
502 v5::ToClientBody::DatabaseTableRowsResponse(resp) => {
503 v4::ToClientBody::DatabaseTableRowsResponse(resp.into())
504 }
505 };
506
507 Ok(Self::V4(v4::ToClient { body }))
508 }
509
510 fn v4_to_v3(self) -> Result<Self> {
511 let Self::V4(data) = self else {
512 bail!("expected inspector protocol v4 ToClient")
513 };
514
515 let body = match data.body {
516 v4::ToClientBody::StateResponse(resp) => v3::ToClientBody::StateResponse(resp.into()),
517 v4::ToClientBody::ConnectionsResponse(resp) => {
518 v3::ToClientBody::ConnectionsResponse(resp.into())
519 }
520 v4::ToClientBody::ActionResponse(resp) => v3::ToClientBody::ActionResponse(resp.into()),
521 v4::ToClientBody::ConnectionsUpdated(update) => {
522 v3::ToClientBody::ConnectionsUpdated(update.into())
523 }
524 v4::ToClientBody::QueueUpdated(update) => v3::ToClientBody::QueueUpdated(update.into()),
525 v4::ToClientBody::StateUpdated(update) => v3::ToClientBody::StateUpdated(update.into()),
526 v4::ToClientBody::WorkflowHistoryUpdated(update) => {
527 v3::ToClientBody::WorkflowHistoryUpdated(update.into())
528 }
529 v4::ToClientBody::RpcsListResponse(resp) => {
530 v3::ToClientBody::RpcsListResponse(resp.into())
531 }
532 v4::ToClientBody::TraceQueryResponse(resp) => {
533 v3::ToClientBody::TraceQueryResponse(resp.into())
534 }
535 v4::ToClientBody::QueueResponse(resp) => v3::ToClientBody::QueueResponse(resp.into()),
536 v4::ToClientBody::WorkflowHistoryResponse(resp) => {
537 v3::ToClientBody::WorkflowHistoryResponse(resp.into())
538 }
539 v4::ToClientBody::WorkflowReplayResponse(_) => {
540 v3::ToClientBody::Error(dropped_error(WORKFLOW_HISTORY_DROPPED_ERROR).into())
541 }
542 v4::ToClientBody::Error(error) => v3::ToClientBody::Error(error.into()),
543 v4::ToClientBody::Init(init) => v3::ToClientBody::Init(init.into()),
544 v4::ToClientBody::DatabaseSchemaResponse(resp) => {
545 v3::ToClientBody::DatabaseSchemaResponse(resp.into())
546 }
547 v4::ToClientBody::DatabaseTableRowsResponse(resp) => {
548 v3::ToClientBody::DatabaseTableRowsResponse(resp.into())
549 }
550 };
551
552 Ok(Self::V3(v3::ToClient { body }))
553 }
554
555 fn v3_to_v2(self) -> Result<Self> {
556 let Self::V3(data) = self else {
557 bail!("expected inspector protocol v3 ToClient")
558 };
559
560 let body = match data.body {
561 v3::ToClientBody::StateResponse(resp) => v2::ToClientBody::StateResponse(resp.into()),
562 v3::ToClientBody::ConnectionsResponse(resp) => {
563 v2::ToClientBody::ConnectionsResponse(resp.into())
564 }
565 v3::ToClientBody::ActionResponse(resp) => v2::ToClientBody::ActionResponse(resp.into()),
566 v3::ToClientBody::ConnectionsUpdated(update) => {
567 v2::ToClientBody::ConnectionsUpdated(update.into())
568 }
569 v3::ToClientBody::QueueUpdated(update) => v2::ToClientBody::QueueUpdated(update.into()),
570 v3::ToClientBody::StateUpdated(update) => v2::ToClientBody::StateUpdated(update.into()),
571 v3::ToClientBody::WorkflowHistoryUpdated(update) => {
572 v2::ToClientBody::WorkflowHistoryUpdated(update.into())
573 }
574 v3::ToClientBody::RpcsListResponse(resp) => {
575 v2::ToClientBody::RpcsListResponse(resp.into())
576 }
577 v3::ToClientBody::TraceQueryResponse(resp) => {
578 v2::ToClientBody::TraceQueryResponse(resp.into())
579 }
580 v3::ToClientBody::QueueResponse(resp) => v2::ToClientBody::QueueResponse(resp.into()),
581 v3::ToClientBody::WorkflowHistoryResponse(resp) => {
582 v2::ToClientBody::WorkflowHistoryResponse(resp.into())
583 }
584 v3::ToClientBody::Error(error) => v2::ToClientBody::Error(error.into()),
585 v3::ToClientBody::Init(init) => v2::ToClientBody::Init(init.into()),
586 v3::ToClientBody::DatabaseSchemaResponse(_)
587 | v3::ToClientBody::DatabaseTableRowsResponse(_) => {
588 v2::ToClientBody::Error(dropped_error(DATABASE_DROPPED_ERROR))
589 }
590 };
591
592 Ok(Self::V2(v2::ToClient { body }))
593 }
594
595 fn v2_to_v1(self) -> Result<Self> {
596 let Self::V2(data) = self else {
597 bail!("expected inspector protocol v2 ToClient")
598 };
599
600 let body = match data.body {
601 v2::ToClientBody::StateResponse(resp) => v1::ToClientBody::StateResponse(resp.into()),
602 v2::ToClientBody::ConnectionsResponse(resp) => {
603 v1::ToClientBody::ConnectionsResponse(resp.into())
604 }
605 v2::ToClientBody::ActionResponse(resp) => v1::ToClientBody::ActionResponse(resp.into()),
606 v2::ToClientBody::ConnectionsUpdated(update) => {
607 v1::ToClientBody::ConnectionsUpdated(update.into())
608 }
609 v2::ToClientBody::StateUpdated(update) => v1::ToClientBody::StateUpdated(update.into()),
610 v2::ToClientBody::RpcsListResponse(resp) => {
611 v1::ToClientBody::RpcsListResponse(resp.into())
612 }
613 v2::ToClientBody::Error(error) => v1::ToClientBody::Error(error.into()),
614 v2::ToClientBody::Init(init) => v1::ToClientBody::Init(v1::Init {
615 connections: init.connections.into_iter().map(Into::into).collect(),
616 events: Vec::new(),
617 state: init.state,
618 is_state_enabled: init.is_state_enabled,
619 rpcs: init.rpcs,
620 is_database_enabled: init.is_database_enabled,
621 }),
622 v2::ToClientBody::QueueUpdated(_) | v2::ToClientBody::QueueResponse(_) => {
623 v1::ToClientBody::Error(dropped_error(QUEUE_DROPPED_ERROR).into())
624 }
625 v2::ToClientBody::WorkflowHistoryUpdated(_)
626 | v2::ToClientBody::WorkflowHistoryResponse(_) => {
627 v1::ToClientBody::Error(dropped_error(WORKFLOW_HISTORY_DROPPED_ERROR).into())
628 }
629 v2::ToClientBody::TraceQueryResponse(_) => {
630 v1::ToClientBody::Error(dropped_error(TRACE_DROPPED_ERROR).into())
631 }
632 };
633
634 Ok(Self::V1(v1::ToClient { body }))
635 }
636}
637
638fn convert_vec<From, To>(values: Vec<From>) -> Vec<To>
639where
640 From: Into<To>,
641{
642 values.into_iter().map(Into::into).collect()
643}
644
645macro_rules! impl_same_fields_pair {
646 ($left:ident, $right:ident, $ty:ident { $($field:ident),+ $(,)? }) => {
647 impl From<$left::$ty> for $right::$ty {
648 fn from(value: $left::$ty) -> Self {
649 Self {
650 $($field: value.$field),+
651 }
652 }
653 }
654
655 impl From<$right::$ty> for $left::$ty {
656 fn from(value: $right::$ty) -> Self {
657 Self {
658 $($field: value.$field),+
659 }
660 }
661 }
662 };
663}
664
665macro_rules! impl_connection_list_pair {
666 ($left:ident, $right:ident, $ty:ident) => {
667 impl From<$left::$ty> for $right::$ty {
668 fn from(value: $left::$ty) -> Self {
669 Self {
670 connections: convert_vec(value.connections),
671 }
672 }
673 }
674
675 impl From<$right::$ty> for $left::$ty {
676 fn from(value: $right::$ty) -> Self {
677 Self {
678 connections: convert_vec(value.connections),
679 }
680 }
681 }
682 };
683}
684
685macro_rules! impl_connections_response_pair {
686 ($left:ident, $right:ident) => {
687 impl From<$left::ConnectionsResponse> for $right::ConnectionsResponse {
688 fn from(value: $left::ConnectionsResponse) -> Self {
689 Self {
690 rid: value.rid,
691 connections: convert_vec(value.connections),
692 }
693 }
694 }
695
696 impl From<$right::ConnectionsResponse> for $left::ConnectionsResponse {
697 fn from(value: $right::ConnectionsResponse) -> Self {
698 Self {
699 rid: value.rid,
700 connections: convert_vec(value.connections),
701 }
702 }
703 }
704 };
705}
706
707macro_rules! impl_queue_status_pair {
708 ($left:ident, $right:ident) => {
709 impl From<$left::QueueStatus> for $right::QueueStatus {
710 fn from(value: $left::QueueStatus) -> Self {
711 Self {
712 size: value.size,
713 max_size: value.max_size,
714 messages: convert_vec(value.messages),
715 truncated: value.truncated,
716 }
717 }
718 }
719
720 impl From<$right::QueueStatus> for $left::QueueStatus {
721 fn from(value: $right::QueueStatus) -> Self {
722 Self {
723 size: value.size,
724 max_size: value.max_size,
725 messages: convert_vec(value.messages),
726 truncated: value.truncated,
727 }
728 }
729 }
730 };
731}
732
733macro_rules! impl_queue_response_pair {
734 ($left:ident, $right:ident) => {
735 impl From<$left::QueueResponse> for $right::QueueResponse {
736 fn from(value: $left::QueueResponse) -> Self {
737 Self {
738 rid: value.rid,
739 status: value.status.into(),
740 }
741 }
742 }
743
744 impl From<$right::QueueResponse> for $left::QueueResponse {
745 fn from(value: $right::QueueResponse) -> Self {
746 Self {
747 rid: value.rid,
748 status: value.status.into(),
749 }
750 }
751 }
752 };
753}
754
755macro_rules! impl_init_pair {
756 ($left:ident, $right:ident) => {
757 impl From<$left::Init> for $right::Init {
758 fn from(value: $left::Init) -> Self {
759 Self {
760 connections: convert_vec(value.connections),
761 state: value.state,
762 is_state_enabled: value.is_state_enabled,
763 rpcs: value.rpcs,
764 is_database_enabled: value.is_database_enabled,
765 queue_size: value.queue_size,
766 workflow_history: value.workflow_history,
767 is_workflow_enabled: value.is_workflow_enabled,
768 }
769 }
770 }
771
772 impl From<$right::Init> for $left::Init {
773 fn from(value: $right::Init) -> Self {
774 Self {
775 connections: convert_vec(value.connections),
776 state: value.state,
777 is_state_enabled: value.is_state_enabled,
778 rpcs: value.rpcs,
779 is_database_enabled: value.is_database_enabled,
780 queue_size: value.queue_size,
781 workflow_history: value.workflow_history,
782 is_workflow_enabled: value.is_workflow_enabled,
783 }
784 }
785 }
786 };
787}
788
789macro_rules! impl_common_actor_pair {
790 ($left:ident, $right:ident) => {
791 impl_same_fields_pair!($left, $right, PatchStateRequest { state });
792 impl_same_fields_pair!($left, $right, ActionRequest { id, name, args });
793 impl_same_fields_pair!($left, $right, StateRequest { id });
794 impl_same_fields_pair!($left, $right, ConnectionsRequest { id });
795 impl_same_fields_pair!($left, $right, RpcsListRequest { id });
796 impl_same_fields_pair!($left, $right, Connection { id, details });
797 impl_connections_response_pair!($left, $right);
798 impl_connection_list_pair!($left, $right, ConnectionsUpdated);
799 impl_same_fields_pair!(
800 $left,
801 $right,
802 StateResponse {
803 rid,
804 state,
805 is_state_enabled,
806 }
807 );
808 impl_same_fields_pair!($left, $right, ActionResponse { rid, output });
809 impl_same_fields_pair!($left, $right, StateUpdated { state });
810 impl_same_fields_pair!($left, $right, RpcsListResponse { rid, rpcs });
811 impl_same_fields_pair!($left, $right, Error { message });
812 };
813}
814
815macro_rules! impl_queue_workflow_pair {
816 ($left:ident, $right:ident) => {
817 impl_same_fields_pair!(
818 $left,
819 $right,
820 TraceQueryRequest {
821 id,
822 start_ms,
823 end_ms,
824 limit,
825 }
826 );
827 impl_same_fields_pair!($left, $right, TraceQueryResponse { rid, payload });
828 impl_same_fields_pair!($left, $right, QueueRequest { id, limit });
829 impl_same_fields_pair!(
830 $left,
831 $right,
832 QueueMessageSummary {
833 id,
834 name,
835 created_at_ms,
836 }
837 );
838 impl_queue_status_pair!($left, $right);
839 impl_queue_response_pair!($left, $right);
840 impl_same_fields_pair!($left, $right, QueueUpdated { queue_size });
841 impl_same_fields_pair!($left, $right, WorkflowHistoryRequest { id });
842 impl_same_fields_pair!(
843 $left,
844 $right,
845 WorkflowHistoryResponse {
846 rid,
847 history,
848 is_workflow_enabled,
849 }
850 );
851 impl_same_fields_pair!($left, $right, WorkflowHistoryUpdated { history });
852 impl_init_pair!($left, $right);
853 };
854}
855
856macro_rules! impl_database_pair {
857 ($left:ident, $right:ident) => {
858 impl_same_fields_pair!($left, $right, DatabaseSchemaRequest { id });
859 impl_same_fields_pair!($left, $right, DatabaseSchemaResponse { rid, schema });
860 impl_same_fields_pair!(
861 $left,
862 $right,
863 DatabaseTableRowsRequest {
864 id,
865 table,
866 limit,
867 offset,
868 }
869 );
870 impl_same_fields_pair!($left, $right, DatabaseTableRowsResponse { rid, result });
871 };
872}
873
874impl_common_actor_pair!(v1, v2);
875impl_common_actor_pair!(v2, v3);
876impl_common_actor_pair!(v3, v4);
877impl_common_actor_pair!(v4, v5);
878impl_queue_workflow_pair!(v2, v3);
879impl_queue_workflow_pair!(v3, v4);
880impl_database_pair!(v3, v4);
881impl_database_pair!(v4, v5);
882
883impl_same_fields_pair!(
888 v4,
889 v5,
890 TraceQueryRequest {
891 id,
892 start_ms,
893 end_ms,
894 limit,
895 }
896);
897impl_same_fields_pair!(v4, v5, TraceQueryResponse { rid, payload });
898impl_same_fields_pair!(v4, v5, QueueRequest { id, limit });
899impl_same_fields_pair!(
900 v4,
901 v5,
902 QueueMessageSummary {
903 id,
904 name,
905 created_at_ms,
906 }
907);
908impl_queue_status_pair!(v4, v5);
909impl_queue_response_pair!(v4, v5);
910impl_same_fields_pair!(v4, v5, QueueUpdated { queue_size });
911impl_same_fields_pair!(v4, v5, WorkflowHistoryRequest { id });
912impl_same_fields_pair!(
913 v4,
914 v5,
915 WorkflowHistoryResponse {
916 rid,
917 history,
918 is_workflow_enabled,
919 }
920);
921impl_same_fields_pair!(v4, v5, WorkflowHistoryUpdated { history });
922impl_same_fields_pair!(v4, v5, WorkflowReplayRequest { id, entry_id });
923impl_same_fields_pair!(
924 v4,
925 v5,
926 WorkflowReplayResponse {
927 rid,
928 history,
929 is_workflow_enabled,
930 }
931);
932
933impl From<v4::ToServerBody> for v5::ToServerBody {
934 fn from(value: v4::ToServerBody) -> Self {
935 match value {
936 v4::ToServerBody::PatchStateRequest(req) => Self::PatchStateRequest(req.into()),
937 v4::ToServerBody::StateRequest(req) => Self::StateRequest(req.into()),
938 v4::ToServerBody::ConnectionsRequest(req) => Self::ConnectionsRequest(req.into()),
939 v4::ToServerBody::ActionRequest(req) => Self::ActionRequest(req.into()),
940 v4::ToServerBody::RpcsListRequest(req) => Self::RpcsListRequest(req.into()),
941 v4::ToServerBody::TraceQueryRequest(req) => Self::TraceQueryRequest(req.into()),
942 v4::ToServerBody::QueueRequest(req) => Self::QueueRequest(req.into()),
943 v4::ToServerBody::WorkflowHistoryRequest(req) => {
944 Self::WorkflowHistoryRequest(req.into())
945 }
946 v4::ToServerBody::WorkflowReplayRequest(req) => Self::WorkflowReplayRequest(req.into()),
947 v4::ToServerBody::DatabaseSchemaRequest(req) => Self::DatabaseSchemaRequest(req.into()),
948 v4::ToServerBody::DatabaseTableRowsRequest(req) => {
949 Self::DatabaseTableRowsRequest(req.into())
950 }
951 }
952 }
953}
954
955impl From<v5::ToServerBody> for v4::ToServerBody {
956 fn from(value: v5::ToServerBody) -> Self {
957 match value {
958 v5::ToServerBody::PatchStateRequest(req) => Self::PatchStateRequest(req.into()),
959 v5::ToServerBody::StateRequest(req) => Self::StateRequest(req.into()),
960 v5::ToServerBody::ConnectionsRequest(req) => Self::ConnectionsRequest(req.into()),
961 v5::ToServerBody::ActionRequest(req) => Self::ActionRequest(req.into()),
962 v5::ToServerBody::RpcsListRequest(req) => Self::RpcsListRequest(req.into()),
963 v5::ToServerBody::TraceQueryRequest(req) => Self::TraceQueryRequest(req.into()),
964 v5::ToServerBody::QueueRequest(req) => Self::QueueRequest(req.into()),
965 v5::ToServerBody::WorkflowHistoryRequest(req) => {
966 Self::WorkflowHistoryRequest(req.into())
967 }
968 v5::ToServerBody::WorkflowReplayRequest(req) => Self::WorkflowReplayRequest(req.into()),
969 v5::ToServerBody::DatabaseSchemaRequest(req) => Self::DatabaseSchemaRequest(req.into()),
970 v5::ToServerBody::DatabaseTableRowsRequest(req) => {
971 Self::DatabaseTableRowsRequest(req.into())
972 }
973 }
974 }
975}
976
977impl From<v4::ToServer> for v5::ToServer {
978 fn from(value: v4::ToServer) -> Self {
979 Self {
980 body: value.body.into(),
981 }
982 }
983}
984
985impl From<v5::ToServer> for v4::ToServer {
986 fn from(value: v5::ToServer) -> Self {
987 Self {
988 body: value.body.into(),
989 }
990 }
991}
992
993impl From<v2::ToServerBody> for v3::ToServerBody {
994 fn from(value: v2::ToServerBody) -> Self {
995 match value {
996 v2::ToServerBody::PatchStateRequest(req) => Self::PatchStateRequest(req.into()),
997 v2::ToServerBody::StateRequest(req) => Self::StateRequest(req.into()),
998 v2::ToServerBody::ConnectionsRequest(req) => Self::ConnectionsRequest(req.into()),
999 v2::ToServerBody::ActionRequest(req) => Self::ActionRequest(req.into()),
1000 v2::ToServerBody::RpcsListRequest(req) => Self::RpcsListRequest(req.into()),
1001 v2::ToServerBody::TraceQueryRequest(req) => Self::TraceQueryRequest(req.into()),
1002 v2::ToServerBody::QueueRequest(req) => Self::QueueRequest(req.into()),
1003 v2::ToServerBody::WorkflowHistoryRequest(req) => {
1004 Self::WorkflowHistoryRequest(req.into())
1005 }
1006 }
1007 }
1008}
1009
1010impl From<v2::ToServer> for v3::ToServer {
1011 fn from(value: v2::ToServer) -> Self {
1012 Self {
1013 body: value.body.into(),
1014 }
1015 }
1016}
1017
1018impl From<v2::ToClientBody> for v3::ToClientBody {
1019 fn from(value: v2::ToClientBody) -> Self {
1020 match value {
1021 v2::ToClientBody::StateResponse(resp) => Self::StateResponse(resp.into()),
1022 v2::ToClientBody::ConnectionsResponse(resp) => Self::ConnectionsResponse(resp.into()),
1023 v2::ToClientBody::ActionResponse(resp) => Self::ActionResponse(resp.into()),
1024 v2::ToClientBody::ConnectionsUpdated(update) => Self::ConnectionsUpdated(update.into()),
1025 v2::ToClientBody::QueueUpdated(update) => Self::QueueUpdated(update.into()),
1026 v2::ToClientBody::StateUpdated(update) => Self::StateUpdated(update.into()),
1027 v2::ToClientBody::WorkflowHistoryUpdated(update) => {
1028 Self::WorkflowHistoryUpdated(update.into())
1029 }
1030 v2::ToClientBody::RpcsListResponse(resp) => Self::RpcsListResponse(resp.into()),
1031 v2::ToClientBody::TraceQueryResponse(resp) => Self::TraceQueryResponse(resp.into()),
1032 v2::ToClientBody::QueueResponse(resp) => Self::QueueResponse(resp.into()),
1033 v2::ToClientBody::WorkflowHistoryResponse(resp) => {
1034 Self::WorkflowHistoryResponse(resp.into())
1035 }
1036 v2::ToClientBody::Error(error) => Self::Error(error.into()),
1037 v2::ToClientBody::Init(init) => Self::Init(init.into()),
1038 }
1039 }
1040}
1041
1042impl From<v2::ToClient> for v3::ToClient {
1043 fn from(value: v2::ToClient) -> Self {
1044 Self {
1045 body: value.body.into(),
1046 }
1047 }
1048}
1049
1050fn dropped_error(message: &str) -> v2::Error {
1051 v2::Error {
1052 message: message.to_owned(),
1053 }
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use super::*;
1059
1060 #[test]
1061 fn v3_database_schema_request_keeps_meaning_when_upgrading_to_v4() {
1062 let request = ToServer::V3(v3::ToServer {
1063 body: v3::ToServerBody::DatabaseSchemaRequest(v3::DatabaseSchemaRequest {
1064 id: Uint(7),
1065 }),
1066 });
1067
1068 let ToServer::V4(upgraded) = ToServer::v3_to_v4(request).unwrap() else {
1069 panic!("expected v4 request")
1070 };
1071
1072 assert!(matches!(
1073 upgraded.body,
1074 v4::ToServerBody::DatabaseSchemaRequest(v4::DatabaseSchemaRequest { id }) if id == Uint(7)
1075 ));
1076 }
1077
1078 fn v5_init_with_tabs() -> v5::Init {
1079 v5::Init {
1080 connections: Vec::new(),
1081 state: None,
1082 is_state_enabled: false,
1083 rpcs: Vec::new(),
1084 is_database_enabled: false,
1085 queue_size: Uint(0),
1086 workflow_history: None,
1087 is_workflow_enabled: false,
1088 tab_config: vec![v5::TabConfigEntry {
1089 id: "custom".to_owned(),
1090 label: Some("Custom".to_owned()),
1091 icon: Some("star".to_owned()),
1092 hidden: false,
1093 }],
1094 }
1095 }
1096
1097 #[test]
1098 fn v5_init_tab_config_drops_when_downgrading_to_v4() {
1099 let response = ToClient::V5(v5::ToClient {
1100 body: v5::ToClientBody::Init(v5_init_with_tabs()),
1101 });
1102
1103 let ToClient::V4(downgraded) = ToClient::v5_to_v4(response).unwrap() else {
1104 panic!("expected v4 response")
1105 };
1106
1107 assert!(matches!(
1110 downgraded.body,
1111 v4::ToClientBody::Init(v4::Init {
1112 is_workflow_enabled: false,
1113 ..
1114 })
1115 ));
1116 }
1117
1118 #[test]
1119 fn v4_init_upgrades_to_v5_with_empty_tab_config() {
1120 let response = ToClient::V4(v4::ToClient {
1121 body: v4::ToClientBody::Init(v4::Init {
1122 connections: Vec::new(),
1123 state: None,
1124 is_state_enabled: false,
1125 rpcs: Vec::new(),
1126 is_database_enabled: false,
1127 queue_size: Uint(0),
1128 workflow_history: None,
1129 is_workflow_enabled: false,
1130 }),
1131 });
1132
1133 let ToClient::V5(upgraded) = ToClient::v4_to_v5(response).unwrap() else {
1134 panic!("expected v5 response")
1135 };
1136
1137 let v5::ToClientBody::Init(init) = upgraded.body else {
1138 panic!("expected Init body")
1139 };
1140 assert!(init.tab_config.is_empty());
1141 }
1142
1143 #[test]
1144 fn v5_init_tab_config_round_trips_at_version_5() {
1145 let original = v5_init_with_tabs();
1146 let encoded = ToClient::V5(v5::ToClient {
1147 body: v5::ToClientBody::Init(original.clone()),
1148 })
1149 .serialize_with_embedded_version(5)
1150 .unwrap();
1151
1152 let decoded =
1155 <ToClient as OwnedVersionedData>::deserialize_with_embedded_version(&encoded).unwrap();
1156 let v5::ToClientBody::Init(init) = decoded.body else {
1157 panic!("expected Init body")
1158 };
1159 assert_eq!(init.tab_config, original.tab_config);
1160 }
1161
1162 #[test]
1163 fn v4_workflow_replay_response_downgrades_to_v3_error() {
1164 let response = ToClient::V4(v4::ToClient {
1165 body: v4::ToClientBody::WorkflowReplayResponse(v4::WorkflowReplayResponse {
1166 rid: Uint(11),
1167 history: Some(b"workflow".to_vec()),
1168 is_workflow_enabled: true,
1169 }),
1170 });
1171
1172 let ToClient::V3(downgraded) = ToClient::v4_to_v3(response).unwrap() else {
1173 panic!("expected v3 response")
1174 };
1175
1176 assert_eq!(
1177 downgraded.body,
1178 v3::ToClientBody::Error(v3::Error {
1179 message: WORKFLOW_HISTORY_DROPPED_ERROR.to_owned(),
1180 })
1181 );
1182 }
1183}