Skip to main content

rivetkit_client_protocol/
versioned.rs

1use anyhow::{Result, bail};
2use vbare::{
3	Bytes, Converter, DecodeError, DecodeErrorKind, Encode, EncodeError, Message, Sink, Versioned,
4};
5
6use crate::generated::{v1, v2, v3, v4};
7
8pub enum ToClient {
9	V1(v1::ToClient),
10	V2(v2::ToClient),
11	V3(v3::ToClient),
12	V4(v4::ToClient),
13}
14
15impl Versioned for ToClient {
16	const LATEST: u16 = 4;
17
18	type Latest = v4::ToClient;
19
20	fn wrap_latest(latest: Self::Latest) -> Self {
21		Self::V4(latest)
22	}
23
24	fn unwrap_latest(self) -> Result<Self::Latest> {
25		match self {
26			Self::V4(data) => Ok(data),
27			_ => bail!("version not latest"),
28		}
29	}
30
31	fn decode_version(version: u16, payload: Bytes) -> std::result::Result<Self, DecodeError> {
32		match version {
33			1 => Ok(Self::V1(Message::decode(payload)?)),
34			2 => Ok(Self::V2(Message::decode(payload)?)),
35			3 => Ok(Self::V3(Message::decode(payload)?)),
36			4 => Ok(Self::V4(Message::decode(payload)?)),
37			_ => Err(unsupported_version(format!(
38				"invalid client protocol version: {version}"
39			))),
40		}
41	}
42
43	fn encode_version<S: Sink>(
44		&self,
45		version: u16,
46		out: &mut S,
47	) -> std::result::Result<(), EncodeError> {
48		match (self, version) {
49			(Self::V1(data), 1) => {
50				data.encode(out);
51				Ok(())
52			}
53			(Self::V2(data), 2) => {
54				data.encode(out);
55				Ok(())
56			}
57			(Self::V3(data), 3) => {
58				data.encode(out);
59				Ok(())
60			}
61			(Self::V4(data), 4) => {
62				data.encode(out);
63				Ok(())
64			}
65			(_, version) => Err(EncodeError::UnsupportedVersion(version)),
66		}
67	}
68
69	const UPGRADES: &'static [Converter<Self>] = &[Self::v1_to_v2, Self::v2_to_v3, Self::v3_to_v4];
70
71	const DOWNGRADES: &'static [Converter<Self>] =
72		&[Self::v4_to_v3, Self::v3_to_v2, Self::v2_to_v1];
73}
74
75impl ToClient {
76	fn v1_to_v2(self) -> Result<Self> {
77		let Self::V1(data) = self else {
78			bail!("expected client protocol v1 ToClient")
79		};
80
81		let body = match data.body {
82			v1::ToClientBody::Init(init) => v2::ToClientBody::Init(v2::Init {
83				actor_id: init.actor_id,
84				connection_id: init.connection_id,
85			}),
86			v1::ToClientBody::Error(error) => v2::ToClientBody::Error(v2::Error {
87				group: error.group,
88				code: error.code,
89				message: error.message,
90				metadata: error.metadata,
91				action_id: error.action_id,
92			}),
93			v1::ToClientBody::ActionResponse(response) => {
94				v2::ToClientBody::ActionResponse(v2::ActionResponse {
95					id: response.id,
96					output: response.output,
97				})
98			}
99			v1::ToClientBody::Event(event) => v2::ToClientBody::Event(v2::Event {
100				name: event.name,
101				args: event.args,
102			}),
103		};
104
105		Ok(Self::V2(v2::ToClient { body }))
106	}
107
108	fn v2_to_v3(self) -> Result<Self> {
109		let Self::V2(data) = self else {
110			bail!("expected client protocol v2 ToClient")
111		};
112		Ok(Self::V3(data.into()))
113	}
114
115	fn v3_to_v4(self) -> Result<Self> {
116		let Self::V3(data) = self else {
117			bail!("expected client protocol v3 ToClient")
118		};
119
120		let body = match data.body {
121			v3::ToClientBody::Init(init) => v4::ToClientBody::Init(v4::Init {
122				actor_id: init.actor_id,
123				connection_id: init.connection_id,
124			}),
125			v3::ToClientBody::Error(error) => v4::ToClientBody::Error(v4::Error {
126				group: error.group,
127				code: error.code,
128				message: error.message,
129				metadata: error.metadata,
130				action_id: error.action_id,
131				actor: None,
132			}),
133			v3::ToClientBody::ActionResponse(response) => {
134				v4::ToClientBody::ActionResponse(v4::ActionResponse {
135					id: response.id,
136					output: response.output,
137				})
138			}
139			v3::ToClientBody::Event(event) => v4::ToClientBody::Event(v4::Event {
140				name: event.name,
141				args: event.args,
142			}),
143		};
144
145		Ok(Self::V4(v4::ToClient { body }))
146	}
147
148	fn v4_to_v3(self) -> Result<Self> {
149		let Self::V4(data) = self else {
150			bail!("expected client protocol v4 ToClient")
151		};
152
153		let body = match data.body {
154			v4::ToClientBody::Init(init) => v3::ToClientBody::Init(v3::Init {
155				actor_id: init.actor_id,
156				connection_id: init.connection_id,
157			}),
158			v4::ToClientBody::Error(error) => v3::ToClientBody::Error(v3::Error {
159				group: error.group,
160				code: error.code,
161				message: error.message,
162				metadata: error.metadata,
163				action_id: error.action_id,
164			}),
165			v4::ToClientBody::ActionResponse(response) => {
166				v3::ToClientBody::ActionResponse(v3::ActionResponse {
167					id: response.id,
168					output: response.output,
169				})
170			}
171			v4::ToClientBody::Event(event) => v3::ToClientBody::Event(v3::Event {
172				name: event.name,
173				args: event.args,
174			}),
175		};
176
177		Ok(Self::V3(v3::ToClient { body }))
178	}
179
180	fn v3_to_v2(self) -> Result<Self> {
181		let Self::V3(data) = self else {
182			bail!("expected client protocol v3 ToClient")
183		};
184		Ok(Self::V2(data.into()))
185	}
186
187	fn v2_to_v1(self) -> Result<Self> {
188		let Self::V2(data) = self else {
189			bail!("expected client protocol v2 ToClient")
190		};
191
192		let body = match data.body {
193			v2::ToClientBody::Init(init) => v1::ToClientBody::Init(v1::Init {
194				actor_id: init.actor_id,
195				connection_id: init.connection_id,
196				connection_token: String::new().into(),
197			}),
198			v2::ToClientBody::Error(error) => v1::ToClientBody::Error(v1::Error {
199				group: error.group,
200				code: error.code,
201				message: error.message,
202				metadata: error.metadata,
203				action_id: error.action_id,
204			}),
205			v2::ToClientBody::ActionResponse(response) => {
206				v1::ToClientBody::ActionResponse(v1::ActionResponse {
207					id: response.id,
208					output: response.output,
209				})
210			}
211			v2::ToClientBody::Event(event) => v1::ToClientBody::Event(v1::Event {
212				name: event.name,
213				args: event.args,
214			}),
215		};
216
217		Ok(Self::V1(v1::ToClient { body }))
218	}
219}
220
221macro_rules! impl_same_fields_pair {
222	($left:ident, $right:ident, $ty:ident { $($field:ident),+ $(,)? }) => {
223		impl From<$left::$ty> for $right::$ty {
224			fn from(value: $left::$ty) -> Self {
225				Self {
226					$($field: value.$field),+
227				}
228			}
229		}
230
231		impl From<$right::$ty> for $left::$ty {
232			fn from(value: $right::$ty) -> Self {
233				Self {
234					$($field: value.$field),+
235				}
236			}
237		}
238	};
239}
240
241macro_rules! impl_to_server_pair {
242	($left:ident, $right:ident) => {
243		impl From<$left::ToServerBody> for $right::ToServerBody {
244			fn from(value: $left::ToServerBody) -> Self {
245				match value {
246					$left::ToServerBody::ActionRequest(request) => {
247						Self::ActionRequest(request.into())
248					}
249					$left::ToServerBody::SubscriptionRequest(request) => {
250						Self::SubscriptionRequest(request.into())
251					}
252				}
253			}
254		}
255
256		impl From<$right::ToServerBody> for $left::ToServerBody {
257			fn from(value: $right::ToServerBody) -> Self {
258				match value {
259					$right::ToServerBody::ActionRequest(request) => {
260						Self::ActionRequest(request.into())
261					}
262					$right::ToServerBody::SubscriptionRequest(request) => {
263						Self::SubscriptionRequest(request.into())
264					}
265				}
266			}
267		}
268
269		impl From<$left::ToServer> for $right::ToServer {
270			fn from(value: $left::ToServer) -> Self {
271				Self {
272					body: value.body.into(),
273				}
274			}
275		}
276
277		impl From<$right::ToServer> for $left::ToServer {
278			fn from(value: $right::ToServer) -> Self {
279				Self {
280					body: value.body.into(),
281				}
282			}
283		}
284	};
285}
286
287macro_rules! impl_common_pair {
288	($left:ident, $right:ident) => {
289		impl_same_fields_pair!($left, $right, ActionRequest { id, name, args });
290		impl_same_fields_pair!(
291			$left,
292			$right,
293			SubscriptionRequest {
294				event_name,
295				subscribe,
296			}
297		);
298		impl_to_server_pair!($left, $right);
299		impl_same_fields_pair!($left, $right, HttpActionRequest { args });
300		impl_same_fields_pair!($left, $right, HttpActionResponse { output });
301		impl_same_fields_pair!($left, $right, HttpResolveResponse { actor_id });
302	};
303}
304
305macro_rules! impl_to_client_v2_v3_pair {
306	() => {
307		impl_same_fields_pair!(
308			v2,
309			v3,
310			Init {
311				actor_id,
312				connection_id,
313			}
314		);
315		impl_same_fields_pair!(
316			v2,
317			v3,
318			Error {
319				group,
320				code,
321				message,
322				metadata,
323				action_id,
324			}
325		);
326		impl_same_fields_pair!(v2, v3, ActionResponse { id, output });
327		impl_same_fields_pair!(v2, v3, Event { name, args });
328
329		impl From<v2::ToClientBody> for v3::ToClientBody {
330			fn from(value: v2::ToClientBody) -> Self {
331				match value {
332					v2::ToClientBody::Init(init) => Self::Init(init.into()),
333					v2::ToClientBody::Error(error) => Self::Error(error.into()),
334					v2::ToClientBody::ActionResponse(response) => {
335						Self::ActionResponse(response.into())
336					}
337					v2::ToClientBody::Event(event) => Self::Event(event.into()),
338				}
339			}
340		}
341
342		impl From<v3::ToClientBody> for v2::ToClientBody {
343			fn from(value: v3::ToClientBody) -> Self {
344				match value {
345					v3::ToClientBody::Init(init) => Self::Init(init.into()),
346					v3::ToClientBody::Error(error) => Self::Error(error.into()),
347					v3::ToClientBody::ActionResponse(response) => {
348						Self::ActionResponse(response.into())
349					}
350					v3::ToClientBody::Event(event) => Self::Event(event.into()),
351				}
352			}
353		}
354
355		impl From<v2::ToClient> for v3::ToClient {
356			fn from(value: v2::ToClient) -> Self {
357				Self {
358					body: value.body.into(),
359				}
360			}
361		}
362
363		impl From<v3::ToClient> for v2::ToClient {
364			fn from(value: v3::ToClient) -> Self {
365				Self {
366					body: value.body.into(),
367				}
368			}
369		}
370	};
371}
372
373impl_common_pair!(v1, v2);
374impl_common_pair!(v2, v3);
375impl_common_pair!(v3, v4);
376impl_to_client_v2_v3_pair!();
377impl_same_fields_pair!(
378	v1,
379	v2,
380	HttpResponseError {
381		group,
382		code,
383		message,
384		metadata,
385	}
386);
387impl_same_fields_pair!(
388	v2,
389	v3,
390	HttpResponseError {
391		group,
392		code,
393		message,
394		metadata,
395	}
396);
397impl_same_fields_pair!(
398	v3,
399	v4,
400	HttpQueueSendRequest {
401		body,
402		name,
403		wait,
404		timeout,
405	}
406);
407impl_same_fields_pair!(v3, v4, HttpQueueSendResponse { status, response });
408
409macro_rules! impl_versioned_manual {
410	($name:ident, $latest_ty:path, $v1_ty:path, $v2_ty:path, $v3_ty:path, $v4_ty:path) => {
411		pub enum $name {
412			V1($v1_ty),
413			V2($v2_ty),
414			V3($v3_ty),
415			V4($v4_ty),
416		}
417
418		impl Versioned for $name {
419			const LATEST: u16 = 4;
420
421			type Latest = $latest_ty;
422
423			fn wrap_latest(latest: Self::Latest) -> Self {
424				Self::V4(latest)
425			}
426
427			fn unwrap_latest(self) -> Result<Self::Latest> {
428				match self {
429					Self::V4(data) => Ok(data),
430					_ => bail!("version not latest"),
431				}
432			}
433
434			fn decode_version(
435				version: u16,
436				payload: Bytes,
437			) -> std::result::Result<Self, DecodeError> {
438				match version {
439					1 => Ok(Self::V1(Message::decode(payload)?)),
440					2 => Ok(Self::V2(Message::decode(payload)?)),
441					3 => Ok(Self::V3(Message::decode(payload)?)),
442					4 => Ok(Self::V4(Message::decode(payload)?)),
443					_ => Err(unsupported_version(format!(
444						"invalid client protocol version for {}: {version}",
445						stringify!($name)
446					))),
447				}
448			}
449
450			fn encode_version<S: Sink>(
451				&self,
452				version: u16,
453				out: &mut S,
454			) -> std::result::Result<(), EncodeError> {
455				match (self, version) {
456					(Self::V1(data), 1) => {
457						data.encode(out);
458						Ok(())
459					}
460					(Self::V2(data), 2) => {
461						data.encode(out);
462						Ok(())
463					}
464					(Self::V3(data), 3) => {
465						data.encode(out);
466						Ok(())
467					}
468					(Self::V4(data), 4) => {
469						data.encode(out);
470						Ok(())
471					}
472					(_, version) => Err(EncodeError::UnsupportedVersion(version)),
473				}
474			}
475
476			const UPGRADES: &'static [Converter<Self>] =
477				&[Self::v1_to_v2, Self::v2_to_v3, Self::v3_to_v4];
478
479			const DOWNGRADES: &'static [Converter<Self>] =
480				&[Self::v4_to_v3, Self::v3_to_v2, Self::v2_to_v1];
481		}
482
483		impl $name {
484			fn v1_to_v2(self) -> Result<Self> {
485				let Self::V1(data) = self else {
486					bail!("expected client protocol v1 {}", stringify!($name))
487				};
488				Ok(Self::V2(data.into()))
489			}
490
491			fn v2_to_v3(self) -> Result<Self> {
492				let Self::V2(data) = self else {
493					bail!("expected client protocol v2 {}", stringify!($name))
494				};
495				Ok(Self::V3(data.into()))
496			}
497
498			fn v3_to_v4(self) -> Result<Self> {
499				let Self::V3(data) = self else {
500					bail!("expected client protocol v3 {}", stringify!($name))
501				};
502				Ok(Self::V4(data.into()))
503			}
504
505			fn v4_to_v3(self) -> Result<Self> {
506				let Self::V4(data) = self else {
507					bail!("expected client protocol v4 {}", stringify!($name))
508				};
509				Ok(Self::V3(data.into()))
510			}
511
512			fn v3_to_v2(self) -> Result<Self> {
513				let Self::V3(data) = self else {
514					bail!("expected client protocol v3 {}", stringify!($name))
515				};
516				Ok(Self::V2(data.into()))
517			}
518
519			fn v2_to_v1(self) -> Result<Self> {
520				let Self::V2(data) = self else {
521					bail!("expected client protocol v2 {}", stringify!($name))
522				};
523				Ok(Self::V1(data.into()))
524			}
525		}
526	};
527}
528
529macro_rules! impl_versioned_v3_only {
530	($name:ident, $latest_ty:path) => {
531		pub enum $name {
532			V3(v3::$name),
533			V4($latest_ty),
534		}
535
536		impl Versioned for $name {
537			const LATEST: u16 = 4;
538
539			type Latest = $latest_ty;
540
541			fn wrap_latest(latest: Self::Latest) -> Self {
542				Self::V4(latest)
543			}
544
545			fn unwrap_latest(self) -> Result<Self::Latest> {
546				match self {
547					Self::V4(data) => Ok(data),
548					_ => bail!("version not latest"),
549				}
550			}
551
552			fn decode_version(
553				version: u16,
554				payload: Bytes,
555			) -> std::result::Result<Self, DecodeError> {
556				match version {
557					3 => Ok(Self::V3(Message::decode(payload)?)),
558					4 => Ok(Self::V4(Message::decode(payload)?)),
559					_ => Err(unsupported_version(format!(
560						"{} only exists in client protocol v3, got {version}",
561						stringify!($name)
562					))),
563				}
564			}
565
566			fn encode_version<S: Sink>(
567				&self,
568				version: u16,
569				out: &mut S,
570			) -> std::result::Result<(), EncodeError> {
571				match (self, version) {
572					(Self::V3(data), 3) => {
573						data.encode(out);
574						Ok(())
575					}
576					(Self::V4(data), 4) => {
577						data.encode(out);
578						Ok(())
579					}
580					(_, version) => Err(EncodeError::UnsupportedVersion(version)),
581				}
582			}
583
584			const UPGRADES: &'static [Converter<Self>] = &[Ok, Ok, Self::v3_to_v4];
585
586			const DOWNGRADES: &'static [Converter<Self>] = &[Self::v4_to_v3, Ok, Ok];
587		}
588
589		impl $name {
590			fn v3_to_v4(self) -> Result<Self> {
591				let Self::V3(data) = self else {
592					bail!("expected client protocol v3 {}", stringify!($name))
593				};
594				Ok(Self::V4(data.into()))
595			}
596
597			fn v4_to_v3(self) -> Result<Self> {
598				let Self::V4(data) = self else {
599					bail!("expected client protocol v4 {}", stringify!($name))
600				};
601				Ok(Self::V3(data.into()))
602			}
603		}
604	};
605}
606
607impl_versioned_manual!(
608	ToServer,
609	v4::ToServer,
610	v1::ToServer,
611	v2::ToServer,
612	v3::ToServer,
613	v4::ToServer
614);
615impl_versioned_manual!(
616	HttpActionRequest,
617	v4::HttpActionRequest,
618	v1::HttpActionRequest,
619	v2::HttpActionRequest,
620	v3::HttpActionRequest,
621	v4::HttpActionRequest
622);
623impl_versioned_manual!(
624	HttpActionResponse,
625	v4::HttpActionResponse,
626	v1::HttpActionResponse,
627	v2::HttpActionResponse,
628	v3::HttpActionResponse,
629	v4::HttpActionResponse
630);
631impl_versioned_manual!(
632	HttpResolveResponse,
633	v4::HttpResolveResponse,
634	v1::HttpResolveResponse,
635	v2::HttpResolveResponse,
636	v3::HttpResolveResponse,
637	v4::HttpResolveResponse
638);
639impl_versioned_v3_only!(HttpQueueSendRequest, v4::HttpQueueSendRequest);
640impl_versioned_v3_only!(HttpQueueSendResponse, v4::HttpQueueSendResponse);
641
642pub enum HttpResponseError {
643	V1(v1::HttpResponseError),
644	V2(v2::HttpResponseError),
645	V3(v3::HttpResponseError),
646	V4(v4::HttpResponseError),
647}
648
649impl Versioned for HttpResponseError {
650	const LATEST: u16 = 4;
651
652	type Latest = v4::HttpResponseError;
653
654	fn wrap_latest(latest: Self::Latest) -> Self {
655		Self::V4(latest)
656	}
657
658	fn unwrap_latest(self) -> Result<Self::Latest> {
659		match self {
660			Self::V4(data) => Ok(data),
661			_ => bail!("version not latest"),
662		}
663	}
664
665	fn decode_version(version: u16, payload: Bytes) -> std::result::Result<Self, DecodeError> {
666		match version {
667			1 => Ok(Self::V1(Message::decode(payload)?)),
668			2 => Ok(Self::V2(Message::decode(payload)?)),
669			3 => Ok(Self::V3(Message::decode(payload)?)),
670			4 => Ok(Self::V4(Message::decode(payload)?)),
671			_ => Err(unsupported_version(format!(
672				"invalid client protocol version for HttpResponseError: {version}"
673			))),
674		}
675	}
676
677	fn encode_version<S: Sink>(
678		&self,
679		version: u16,
680		out: &mut S,
681	) -> std::result::Result<(), EncodeError> {
682		match (self, version) {
683			(Self::V1(data), 1) => {
684				data.encode(out);
685				Ok(())
686			}
687			(Self::V2(data), 2) => {
688				data.encode(out);
689				Ok(())
690			}
691			(Self::V3(data), 3) => {
692				data.encode(out);
693				Ok(())
694			}
695			(Self::V4(data), 4) => {
696				data.encode(out);
697				Ok(())
698			}
699			(_, version) => return Err(EncodeError::UnsupportedVersion(version)),
700		}
701	}
702
703	const UPGRADES: &'static [Converter<Self>] = &[Self::v1_to_v2, Self::v2_to_v3, Self::v3_to_v4];
704
705	const DOWNGRADES: &'static [Converter<Self>] =
706		&[Self::v4_to_v3, Self::v3_to_v2, Self::v2_to_v1];
707}
708
709impl HttpResponseError {
710	fn v1_to_v2(self) -> Result<Self> {
711		let Self::V1(data) = self else {
712			bail!("expected client protocol v1 HttpResponseError")
713		};
714		Ok(Self::V2(data.into()))
715	}
716
717	fn v2_to_v3(self) -> Result<Self> {
718		let Self::V2(data) = self else {
719			bail!("expected client protocol v2 HttpResponseError")
720		};
721		Ok(Self::V3(data.into()))
722	}
723
724	fn v3_to_v4(self) -> Result<Self> {
725		let Self::V3(data) = self else {
726			bail!("expected client protocol v3 HttpResponseError")
727		};
728		Ok(Self::V4(v4::HttpResponseError {
729			group: data.group,
730			code: data.code,
731			message: data.message,
732			metadata: data.metadata,
733			actor: None,
734		}))
735	}
736
737	fn v4_to_v3(self) -> Result<Self> {
738		let Self::V4(data) = self else {
739			bail!("expected client protocol v4 HttpResponseError")
740		};
741		Ok(Self::V3(v3::HttpResponseError {
742			group: data.group,
743			code: data.code,
744			message: data.message,
745			metadata: data.metadata,
746		}))
747	}
748
749	fn v3_to_v2(self) -> Result<Self> {
750		let Self::V3(data) = self else {
751			bail!("expected client protocol v3 HttpResponseError")
752		};
753		Ok(Self::V2(data.into()))
754	}
755
756	fn v2_to_v1(self) -> Result<Self> {
757		let Self::V2(data) = self else {
758			bail!("expected client protocol v2 HttpResponseError")
759		};
760		Ok(Self::V1(data.into()))
761	}
762}
763
764/// The error for a version this envelope cannot decode.
765fn unsupported_version(detail: String) -> DecodeError {
766	DecodeError::with_detail(DecodeErrorKind::UnsupportedVersion, 0, detail)
767}