1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
910use bytes::Bytes;
1112/// Denotes that `Self` is wire compatible with type `T`.
13///
14/// You should not implement this yourself, instead use the `wire_compatible!` macro.
15pub unsafe trait WireCompatible<T: prost::Message>: prost::Message + Default {
16/// Converts the type `T` into `Self` by serializing `T` and deserializing as `Self`.
17fn convert(old: &T) -> Self {
18let bytes = old.encode_to_vec();
19// Note: use Bytes to enable possible re-use of the underlying buffer.
20let bytes = Bytes::from(bytes);
21Self::decode(bytes).expect("wire compatible")
22 }
23}
2425// SAFETY: A message type is trivially wire compatible with itself.
26unsafe impl<T: prost::Message + Default + Clone> WireCompatible<T> for T {
27fn convert(old: &Self) -> Self {
28 old.clone()
29 }
30}
3132/// Defines one protobuf type as wire compatible with another.
33///
34/// ```text
35/// wire_compatible!(objects_v28::DatabaseKey with objects_v27::DatabaseKey);
36/// ```
37///
38/// Internally this will implement the `WireCompatible<B> for <A>`, e.g.
39/// `WireCompatible<objects_v27::DatabaseKey> for objects_v28::DatabaseKey` and generate `proptest`
40/// cases that will create arbitrary objects of type `B` and assert they can be deserialized with
41/// type `A`, and vice versa.
42#[macro_export]
43macro_rules! wire_compatible {
44 ($a:ident $(:: $a_sub:ident)* with $b:ident $(:: $b_sub:ident)*) => {
45::static_assertions::assert_impl_all!(
46$a $(::$a_sub)* : ::proptest::arbitrary::Arbitrary, ::prost::Message, Default,
47 );
48::static_assertions::assert_impl_all!(
49$b $(::$b_sub)* : ::proptest::arbitrary::Arbitrary, ::prost::Message, Default,
50 );
5152// SAFETY: Below we assert that these types are wire compatible by generating arbitrary
53 // structs, encoding in one, and then decoding in the other.
54unsafe impl $crate::durable::upgrade::wire_compatible::WireCompatible< $b $(::$b_sub)* > for $a $(::$a_sub)* {}
55unsafe impl $crate::durable::upgrade::wire_compatible::WireCompatible< $a $(::$a_sub)* > for $b $(::$b_sub)* {}
5657::paste::paste! {
58::proptest::proptest! {
59#[mz_ore::test]
60 #[cfg_attr(miri, ignore)] // slow
61fn [<proptest_wire_compat_ $a:snake $(_$a_sub:snake)* _to_ $b:snake $(_$b_sub:snake)* >](a: $a $(::$a_sub)* ) {
62use ::prost::Message;
63let a_bytes = a.encode_to_vec();
64let b_decoded = $b $(::$b_sub)*::decode(&a_bytes[..]);
65::proptest::prelude::prop_assert!(b_decoded.is_ok());
6667// Maybe superfluous, but this is a method called in production.
68let b_decoded = b_decoded.expect("asserted Ok");
69let b_converted: $b $(::$b_sub)* = $crate::durable::upgrade::wire_compatible::WireCompatible::convert(&a);
70assert_eq!(b_decoded, b_converted);
7172let b_bytes = b_decoded.encode_to_vec();
73::proptest::prelude::prop_assert_eq!(a_bytes, b_bytes, "a and b serialize differently");
74 }
7576#[mz_ore::test]
77 #[cfg_attr(miri, ignore)] // slow
78fn [<proptest_wire_compat_ $b:snake $(_$b_sub:snake)* _to_ $a:snake $(_$a_sub:snake)* >](b: $b $(::$b_sub)* ) {
79use ::prost::Message;
80let b_bytes = b.encode_to_vec();
81let a_decoded = $a $(::$a_sub)*::decode(&b_bytes[..]);
82::proptest::prelude::prop_assert!(a_decoded.is_ok());
8384// Maybe superfluous, but this is a method called in production.
85let a_decoded = a_decoded.expect("asserted Ok");
86let a_converted: $a $(::$a_sub)* = $crate::durable::upgrade::wire_compatible::WireCompatible::convert(&b);
87assert_eq!(a_decoded, a_converted);
8889let a_bytes = a_decoded.encode_to_vec();
90::proptest::prelude::prop_assert_eq!(a_bytes, b_bytes, "a and b serialize differently");
91 }
92 }
93 }
94 };
95}
96pub use wire_compatible;