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