Skip to main content

sbor/
versioned.rs

1use crate::internal_prelude::*;
2
3/// A trait implemented by versioned types created via [`define_versioned`] and [`define_single_versioned`].
4///
5/// A versioned type is a type wrapping an enum, this enum is the associated type [`Versioned::Versions`],
6/// and contains a variant for each supported version.
7///
8/// This [`Versioned`] type itself is a struct wrapper around this enum, which allows for fully updating
9/// the contained version to [`Versioned::LatestVersion`]. This wrapper is required so that the wrapper
10/// can take ownership of old versions as part of the upgrade process, in order to incrementally update
11/// them using the [`From`] trait.
12pub trait Versioned: AsRef<Self::Versions> + AsMut<Self::Versions> + From<Self::Versions> {
13    /// The type for the enum of versions.
14    type Versions: From<Self>;
15
16    /// The type for the latest content.
17    type LatestVersion;
18
19    /// Returns true if at the latest version.
20    fn is_fully_updated(&self) -> bool;
21
22    /// Updates the latest version in place, and returns a `&mut` to the latest content
23    fn in_place_fully_update_and_as_latest_version_mut(&mut self) -> &mut Self::LatestVersion {
24        self.in_place_fully_update();
25        self.as_latest_version_mut().unwrap()
26    }
27
28    /// Updates to the latest version in place.
29    fn in_place_fully_update(&mut self) -> &mut Self;
30
31    /// Consumes self, updates to the latest version and returns itself.
32    fn fully_update(mut self) -> Self {
33        self.in_place_fully_update();
34        self
35    }
36
37    /// Updates itself to the latest version, then returns the latest content
38    fn fully_update_and_into_latest_version(self) -> Self::LatestVersion;
39
40    /// Constructs a versioned wrapper around the latest content
41    fn from_latest_version(latest: Self::LatestVersion) -> Self;
42
43    /// If the versioned wrapper is at the latest version, it returns
44    /// an immutable reference to the latest content, otherwise it returns `None`.
45    ///
46    /// If you require the latest version unconditionally, consider using
47    /// [`in_place_fully_update_and_as_latest_version_mut`] to update to the latest version first - or, if
48    /// there is only a single version, use [`as_unique_version`].
49    fn as_latest_version(&self) -> Option<&Self::LatestVersion>;
50
51    /// If the versioned wrapper is at the latest version, it returns
52    /// a mutable reference to the latest content, otherwise it returns `None`.
53    ///
54    /// If you require the latest version unconditionally, consider using
55    /// [`in_place_fully_update_and_as_latest_version_mut`] to update to the latest version first  - or, if
56    /// there is only a single version, use [`as_unique_version_mut`].
57    fn as_latest_version_mut(&mut self) -> Option<&mut Self::LatestVersion>;
58
59    /// Gets a reference the inner versions enum, for e.g. matching on the enum.
60    ///
61    /// This is essentially a clearer alias for `as_ref`.
62    fn as_versions(&self) -> &Self::Versions;
63
64    /// Gets a mutable reference the inner versions enum, for e.g. matching on the enum.
65    ///
66    /// This is essentially a clearer alias for `as_mut`.
67    fn as_versions_mut(&mut self) -> &mut Self::Versions;
68
69    /// Removes the upgradable wrapper to get at the inner versions enum, for e.g. matching on the enum.
70    fn into_versions(self) -> Self::Versions;
71
72    /// Creates a new Versioned wrapper from a given specific version.
73    fn from_versions(version: Self::Versions) -> Self;
74}
75
76/// A trait for Versioned types which only have a single version.
77///
78/// This enables a number of special-cased methods to be implemented which are only possible when there
79/// is only one version.
80pub trait UniqueVersioned: Versioned {
81    /// Returns an immutable reference to (currently) the only possible version of the inner content.
82    fn as_unique_version(&self) -> &Self::LatestVersion;
83
84    /// Returns a mutable reference to (currently) the only possible version of the inner content.
85    ///
86    /// This is somewhat equivalent to `in_place_fully_update_and_as_latest_version_mut`, but doesn't need to do
87    /// any updating, so can be used where logical correctness requires there to be a unique version,
88    /// requires no updating, or simply for slightly better performance.
89    fn as_unique_version_mut(&mut self) -> &mut Self::LatestVersion;
90
91    /// Returns the (currently) only possible version of the inner content.
92    ///
93    /// This is somewhat equivalent to `fully_update_and_into_latest_version`, but doesn't need to do
94    /// any updating, so can be used where logical correctness requires there to be a unique version,
95    /// requires no updating, or simply for slightly better performance.
96    fn into_unique_version(self) -> Self::LatestVersion;
97
98    /// Creates the versioned wrapper from the (currently) only possible version.
99    ///
100    /// This is equivalent to `from_latest_version`, but useful to use instead if your logic's correctness
101    /// is dependent on there only being a single version. If another version gets added, this
102    /// method will give a compile error.
103    fn from_unique_version(unique_version: Self::LatestVersion) -> Self;
104}
105
106/// This macro is intended for creating a data model which supports versioning.
107/// This is useful for creating an SBOR data model which can be updated in future.
108///
109/// In future, the type can be converted to `define_versioned`, enum variants can
110/// be added, and automatically mapped to the latest version.
111///
112/// This macro is just a simpler wrapper around the [`define_versioned`] macro,
113/// for use when there's just a single version.
114///
115/// ## Example usage
116///
117/// ```rust
118/// use sbor::prelude::*;
119///
120/// #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
121/// pub struct FooV1 {
122///    bar: u8,
123/// }
124///
125/// define_single_versioned! {
126///    #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
127///    pub VersionedFoo(FooVersions) => Foo = FooV1
128/// }
129///
130/// // `Foo` is created as an alias for `FooV1`
131/// let a = Foo { bar: 42 }.into_versioned();
132/// let a3 = VersionedFoo::from(FooVersions::V1(FooV1 { bar: 42 }));
133/// let a2 = VersionedFoo::from_unique_version(Foo { bar: 42 });
134///
135/// assert_eq!(a, a2);
136/// assert_eq!(a2, a3);
137/// assert_eq!(42, a.as_unique_version().bar);
138/// ```
139///
140/// ## Advanced attribute handling
141///
142/// Note that the provided attributes get applied to _both_ the outer "Versioned" type,
143/// and the inner "Versions" type. To only apply to one type, you can include the
144/// `outer_attributes` optional argument and/or the `inner_attributes` optional argument:
145/// ```
146/// # use sbor::prelude::*;
147/// # #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
148/// # pub struct FooV1;
149/// define_single_versioned! {
150///    #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
151///    pub VersionedFoo(FooVersions) => Foo = FooV1,
152///    outer_attributes: [
153///        #[sbor(type_name = "MyVersionedFoo")]
154///    ],
155///    inner_attributes: [
156///        #[sbor(type_name = "MyFooVersions")]
157///    ],
158/// }
159/// ```
160#[macro_export]
161macro_rules! define_single_versioned {
162    (
163        $(#[$attributes:meta])*
164        $vis:vis $versioned_name:ident(
165            $versions_name:ident
166        )
167        $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
168        =>
169        $latest_version_alias:ty = $latest_version_type:ty
170        $(, outer_attributes: [
171            $(#[$outer_attributes:meta])*
172        ])?
173        $(, inner_attributes: [
174            $(#[$inner_attributes:meta])*
175        ])?
176        $(,)?
177    ) => {
178        $crate::define_versioned!(
179            $(#[$attributes])*
180            $vis $versioned_name($versions_name)
181            $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)?
182            {
183                previous_versions: [],
184                latest_version: {
185                    1 => $latest_version_alias = $latest_version_type
186                },
187            }
188            $(, outer_attributes: [
189                $(#[$outer_attributes])*
190            ])?
191            $(, inner_attributes: [
192                $(#[$inner_attributes])*
193            ])?
194        );
195
196        $crate::paste::paste! {
197            #[allow(dead_code)]
198            impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
199            UniqueVersioned
200            for $versioned_name $(< $( $lt ),+ >)?
201            {
202                fn as_unique_version(&self) -> &Self::LatestVersion {
203                    match self.as_ref() {
204                        $versions_name $(::< $( $lt ),+ >)? ::V1(content) => content,
205                    }
206                }
207
208                fn as_unique_version_mut(&mut self) -> &mut Self::LatestVersion {
209                    match self.as_mut() {
210                        $versions_name $(::< $( $lt ),+ >)? ::V1(content) => content,
211                    }
212                }
213
214                fn into_unique_version(self) -> Self::LatestVersion {
215                    match $versions_name $(::< $( $lt ),+ >)? ::from(self) {
216                        $versions_name $(::< $( $lt ),+ >)? ::V1(content) => content,
217                    }
218                }
219
220                fn from_unique_version(content: Self::LatestVersion) -> Self {
221                    $versions_name $(::< $( $lt ),+ >)? ::V1(content).into()
222                }
223            }
224        }
225    };
226}
227
228/// This macro is intended for creating a data model which supports versioning.
229/// This is useful for creating an SBOR data model which can be updated in future.
230/// In future, enum variants can be added, and automatically mapped to.
231///
232/// NOTE: A circular version update chain will be an infinite loop at runtime. Be careful.
233///
234/// In the future, this may become a programmatic macro to support better error handling /
235/// edge case detection, and opting into more explicit SBOR handling.
236///
237/// ## Example usage
238///
239/// ```rust
240/// use sbor::prelude::*;
241///
242/// #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
243/// pub struct FooV1 {
244///    bar: u8,
245/// }
246///
247/// #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
248/// pub struct FooV2 {
249///    bar: u8,
250///    baz: Option<u8>,
251/// }
252///
253/// impl From<FooV1> for FooV2 {
254///     fn from(value: FooV1) -> FooV2 {
255///         FooV2 {
256///             bar: value.bar,
257///             // Could also use `value.bar` as sensible default during inline update
258///             baz: None,
259///         }
260///     }
261/// }
262///
263/// define_versioned!(
264///     #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
265///     VersionedFoo(FooVersions) {
266///         previous_versions: [
267///             1 => FooV1: { updates_to: 2 },
268///         ],
269///         latest_version: {
270///             2 => Foo = FooV2,
271///         },
272///     }
273/// );
274///
275/// let mut a = FooV1 { bar: 42 }.into_versioned();
276/// let equivalent_a = VersionedFoo::from(FooVersions::V1(FooV1 { bar: 42 }));
277/// assert_eq!(a, equivalent_a);
278///
279/// // `Foo` is created as an alias for the latest content, `FooV2`
280/// let b = VersionedFoo::from(FooVersions::V2(Foo { bar: 42, baz: None }));
281///
282/// assert_ne!(a, b);
283/// assert_eq!(&*a.in_place_fully_update_and_as_latest_version_mut(), b.as_latest_version().unwrap());
284///
285/// // After a call to `a.in_place_fully_update_and_as_latest_version_mut()`, `a` has now been updated:
286/// assert_eq!(a, b);
287/// ```
288///
289/// ## Advanced attribute handling
290///
291/// The provided attributes get applied to _both_ the outer "Versioned" type,
292/// and the inner "Versions" type. To only apply to one type, you can include the
293/// `outer_attributes` optional argument and/or the `inner_attributes` optional argument:
294/// ```
295/// # use sbor::prelude::*;
296/// # #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
297/// # pub struct FooV1;
298/// # #[derive(Clone, PartialEq, Eq, Hash, Debug, Sbor)]
299/// # pub struct FooV2;
300/// # impl From<FooV1> for FooV2 {
301/// #    fn from(value: FooV1) -> FooV2 {
302/// #        FooV2
303/// #    }
304/// # }
305///
306/// define_versioned! {
307///     #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
308///     VersionedFoo(FooVersions) {
309///         previous_versions: [
310///             1 => FooV1: { updates_to: 2 },
311///         ],
312///         latest_version: {
313///             2 => Foo = FooV2,
314///         },
315///     }
316///     outer_attributes: [
317///         #[sbor(type_name = "MyVersionedFoo")]
318///     ],
319///     inner_attributes: [
320///         #[sbor(type_name = "MyFooVersions")]
321///     ],
322/// }
323#[macro_export]
324macro_rules! define_versioned {
325    (
326        $(#[$attributes:meta])*
327        $vis:vis $versioned_name:ident($versions_name:ident)
328        // Now match the optional type parameters
329        // See https://stackoverflow.com/questions/41603424/rust-macro-accepting-type-with-generic-parameters
330        $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
331        {
332            $(
333                previous_versions: [
334                    $($version_num:expr => $version_type:ty: { updates_to: $update_to_version_num:expr }),*
335                    $(,)? // Optional trailing comma
336                ],
337            )?
338            latest_version: {
339                $latest_version:expr => $latest_version_alias:ty = $latest_version_type:ty
340                $(,)? // Optional trailing comma
341            }
342            $(,)? // Optional trailing comma
343        }
344        $(,)?
345        $(outer_attributes: [
346            $(#[$outer_attributes:meta])*
347        ])?
348        $(, inner_attributes: [
349            $(#[$inner_attributes:meta])*
350        ])?
351        $(,)?
352    ) => {
353        $crate::prelude::preinterpret! {
354            [!set! #full_generics = $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)?]
355            [!set! #impl_generics = $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?]
356            [!set! #type_generics = $(< $( $lt ),+ >)?]
357            [!set! #versioned_type = $versioned_name $(< $( $lt ),+ >)?]
358            [!set! #versioned_type_path = $versioned_name $(::< $( $lt ),+ >)?]
359            [!set! #versions_type = $versions_name $(< $( $lt ),+ >)?]
360            [!set! #versions_type_path = $versions_name $(::< $( $lt ),+ >)?]
361            [!set! #permit_sbor_attribute_alias = [!ident! $versioned_name _PermitSborAttributes]]
362
363            #[allow(dead_code)]
364            $vis type $latest_version_alias = $latest_version_type;
365
366            use $crate::PermitSborAttributes as #permit_sbor_attribute_alias;
367
368            #[derive(#permit_sbor_attribute_alias)]
369            $(#[$attributes])*
370            $($(#[$outer_attributes])*)?
371            // Needs to go below $attributes so that a #[derive(Sbor)] in the attributes can see it.
372            #[sbor(as_type = [!string! #versions_type])]
373            /// If you wish to get access to match on the versions, use `.as_ref()` or `.as_mut()`.
374            $vis struct $versioned_name #full_generics
375            {
376                inner: Option<#versions_type>,
377            }
378
379            impl #impl_generics #versioned_type
380            {
381                pub fn new(inner: #versions_type) -> Self {
382                    Self {
383                        inner: Some(inner),
384                    }
385                }
386            }
387
388            impl #impl_generics AsRef<#versions_type> for #versioned_type
389            {
390                fn as_ref(&self) -> &#versions_type {
391                    self.inner.as_ref().unwrap()
392                }
393            }
394
395            impl #impl_generics AsMut<#versions_type> for #versioned_type
396            {
397                fn as_mut(&mut self) -> &mut #versions_type {
398                    self.inner.as_mut().unwrap()
399                }
400            }
401
402            impl #impl_generics From<#versions_type> for #versioned_type
403            {
404                fn from(value: #versions_type) -> Self {
405                    Self::new(value)
406                }
407            }
408
409            impl #impl_generics From<#versioned_type> for #versions_type
410            {
411                fn from(value: #versioned_type) -> Self {
412                    value.inner.unwrap()
413                }
414            }
415
416            impl #impl_generics Versioned for #versioned_type
417            {
418                type Versions = #versions_type;
419                type LatestVersion = $latest_version_type;
420
421                fn is_fully_updated(&self) -> bool {
422                    self.as_ref().is_fully_updated()
423                }
424
425                fn in_place_fully_update(&mut self) -> &mut Self {
426                    if !self.is_fully_updated() {
427                        let current = self.inner.take().unwrap();
428                        self.inner = Some(current.fully_update());
429                    }
430                    self
431                }
432
433                fn fully_update_and_into_latest_version(self) -> Self::LatestVersion {
434                    self.inner.unwrap().fully_update_and_into_latest_version()
435                }
436
437                /// Constructs the versioned enum from the latest content
438                fn from_latest_version(latest: Self::LatestVersion) -> Self {
439                    Self::new(latest.into())
440                }
441
442                fn as_latest_version(&self) -> Option<&Self::LatestVersion> {
443                    self.as_ref().as_latest_version()
444                }
445
446                fn as_latest_version_mut(&mut self) -> Option<&mut Self::LatestVersion> {
447                    self.as_mut().as_latest_version_mut()
448                }
449
450                fn as_versions(&self) -> &Self::Versions {
451                    self.as_ref()
452                }
453
454                fn as_versions_mut(&mut self) -> &mut Self::Versions {
455                    self.as_mut()
456                }
457
458                fn into_versions(self) -> Self::Versions {
459                    self.inner.unwrap()
460                }
461
462                fn from_versions(version: Self::Versions) -> Self {
463                    Self::new(version)
464                }
465            }
466
467            [!set! #discriminators = [!ident! $versioned_name _discriminators]]
468            #[allow(non_snake_case)]
469            mod #discriminators {
470                // The initial version of this tool used 0-indexed/off-by-one discriminators accidentally.
471                // We're stuck with these now unfortunately...
472                // But we make them explicit in case versions are skipped.
473                $($(
474                    pub const [!ident! VERSION_ $version_num]: u8 = $version_num - 1;
475                )*)?
476                pub const LATEST_VERSION: u8 = $latest_version - 1;
477            }
478
479            #[derive(#permit_sbor_attribute_alias)]
480            $(#[$attributes])*
481            $($(#[$inner_attributes])*)?
482            $vis enum $versions_name #full_generics
483            {
484                $($(
485                    #[sbor(discriminator(#discriminators::[!ident! VERSION_ $version_num]))]
486                    [!ident! V $version_num]($version_type),
487                )*)?
488                #[sbor(discriminator(#discriminators::LATEST_VERSION))]
489                [!ident! V $latest_version]($latest_version_type),
490            }
491
492            #[allow(dead_code)]
493            impl #impl_generics #versions_type
494            {
495                /// Returns if update happened, and the updated versioned enum.
496                fn attempt_single_update(self) -> (bool, Self) {
497                    match self {
498                    $($(
499                        Self::[!ident! V $version_num](value) => (true, Self::[!ident! V $update_to_version_num](value.into())),
500                    )*)?
501                        this @ Self::[!ident! V $latest_version](_) => (false, this),
502                    }
503                }
504
505                fn fully_update(mut self) -> Self {
506                    loop {
507                        let (did_update, updated) = self.attempt_single_update();
508                        if did_update {
509                            // We should try updating
510                            self = updated;
511                        } else {
512                            // We're at latest - return
513                            return updated;
514                        }
515                    }
516                }
517
518                #[allow(unreachable_patterns)]
519                pub fn is_fully_updated(&self) -> bool {
520                    match self {
521                        Self::[!ident! V $latest_version](_) => true,
522                        _ => false,
523                    }
524                }
525
526                #[allow(irrefutable_let_patterns)]
527                fn fully_update_and_into_latest_version(self) -> $latest_version_type {
528                    let Self::[!ident! V $latest_version](latest) = self.fully_update() else {
529                        panic!("Invalid resolved latest version not equal to latest type")
530                    };
531                    return latest;
532                }
533
534                fn from_latest_version(latest: $latest_version_type) -> Self {
535                    Self::[!ident! V $latest_version](latest)
536                }
537
538                #[allow(unreachable_patterns)]
539                fn as_latest_version(&self) -> Option<&$latest_version_type> {
540                    match self {
541                        Self::[!ident! V $latest_version](latest) => Some(latest),
542                        _ => None,
543                    }
544                }
545
546                #[allow(unreachable_patterns)]
547                fn as_latest_version_mut(&mut self) -> Option<&mut $latest_version_type> {
548                    match self {
549                        Self::[!ident! V $latest_version](latest) => Some(latest),
550                        _ => None,
551                    }
552                }
553
554                pub fn into_versioned(self) -> #versioned_type {
555                    #versioned_type_path::new(self)
556                }
557            }
558
559            $($(
560                #[allow(dead_code)]
561                impl #impl_generics From<$version_type> for #versions_type {
562                    fn from(value: $version_type) -> Self {
563                        Self::[!ident! V $version_num](value)
564                    }
565                }
566
567                #[allow(dead_code)]
568                impl #impl_generics From<$version_type> for #versioned_type {
569                    fn from(value: $version_type) -> Self {
570                        Self::new(#versions_type_path::[!ident! V $version_num](value))
571                    }
572                }
573            )*)?
574
575            #[allow(dead_code)]
576            impl #impl_generics From<$latest_version_type> for #versions_type {
577                fn from(value: $latest_version_type) -> Self {
578                    Self::[!ident! V $latest_version](value)
579                }
580            }
581
582            #[allow(dead_code)]
583            impl #impl_generics From<$latest_version_type> for #versioned_type {
584                fn from(value: $latest_version_type) -> Self {
585                    Self::new($versions_name::[!ident! V $latest_version](value))
586                }
587            }
588
589            // This trait is similar to `SborEnumVariantFor<X, Versioned>`, but it's nicer to use as
590            // it's got a better name and can be implemented without needing a specific CustomValueKind.
591            [!set! #version_trait = [!ident! $versioned_name Version]]
592            #[allow(dead_code)]
593            $vis trait #version_trait {
594                // Note: We need to use an explicit associated type to capture the generics.
595                type Versioned: sbor::Versioned;
596
597                const DISCRIMINATOR: u8;
598                type OwnedSborVariant;
599                type BorrowedSborVariant<'a> where Self: 'a;
600
601                /// Can be used to encode the type as a variant under the Versioned type, without
602                /// needing to clone, like this: `encoder.encode(x.as_encodable_variant())`.
603                fn as_encodable_variant(&self) -> Self::BorrowedSborVariant<'_>;
604
605                /// Can be used to decode the type from an encoded variant, like this:
606                /// `X::from_decoded_variant(decoder.decode()?)`.
607                fn from_decoded_variant(variant: Self::OwnedSborVariant) -> Self where Self: core::marker::Sized;
608
609                fn into_versioned(self) -> Self::Versioned;
610            }
611
612            $($(
613                impl #impl_generics #version_trait for $version_type
614                {
615                    type Versioned = #versioned_type;
616
617                    const DISCRIMINATOR: u8 = #discriminators::[!ident! VERSION_ $version_num];
618                    type OwnedSborVariant = sbor::SborFixedEnumVariant::<{ #discriminators::[!ident! VERSION_ $version_num] }, (Self,)>;
619                    type BorrowedSborVariant<'a> = sbor::SborFixedEnumVariant::<{ #discriminators::[!ident! VERSION_ $version_num] }, (&'a Self,)>  where Self: 'a;
620
621                    fn as_encodable_variant(&self) -> Self::BorrowedSborVariant<'_> {
622                        sbor::SborFixedEnumVariant::new((self,))
623                    }
624
625                    fn from_decoded_variant(variant: Self::OwnedSborVariant) -> Self {
626                        variant.into_fields().0
627                    }
628
629                    fn into_versioned(self) -> Self::Versioned {
630                        #versioned_type_path::new(self.into())
631                    }
632                }
633            )*)?
634
635            impl #impl_generics #version_trait for $latest_version_type
636            {
637                type Versioned = $versioned_name #type_generics;
638
639                const DISCRIMINATOR: u8 = #discriminators::LATEST_VERSION;
640                type OwnedSborVariant = sbor::SborFixedEnumVariant::<{ #discriminators::LATEST_VERSION }, (Self,)>;
641                type BorrowedSborVariant<'a> = sbor::SborFixedEnumVariant::<{ #discriminators::LATEST_VERSION }, (&'a Self,)> where Self: 'a;
642
643                fn as_encodable_variant(&self) -> Self::BorrowedSborVariant<'_> {
644                    sbor::SborFixedEnumVariant::new((self,))
645                }
646
647                fn from_decoded_variant(variant: Self::OwnedSborVariant) -> Self {
648                    variant.into_fields().0
649                }
650
651                fn into_versioned(self) -> Self::Versioned {
652                    #versioned_type_path::new(self.into())
653                }
654            }
655        }
656    };
657}
658
659#[cfg(test)]
660mod tests {
661    use super::*;
662    use crate::*;
663
664    crate::define_versioned!(
665        #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
666        VersionedExample(ExampleVersions) {
667            previous_versions: [
668                1 => ExampleV1: { updates_to: 2 },
669                2 => ExampleV2: { updates_to: 4 },
670                3 => ExampleV3: { updates_to: 4 },
671            ],
672            latest_version: {
673                4 => Example = ExampleV4,
674            },
675        }
676    );
677
678    // Define the concrete versions
679    type ExampleV1 = u8;
680    type ExampleV2 = u16;
681
682    #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
683    struct ExampleV3(u16);
684
685    #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
686    struct ExampleV4 {
687        the_value: u16,
688    }
689
690    impl ExampleV4 {
691        pub fn of(value: u16) -> Self {
692            Self { the_value: value }
693        }
694    }
695
696    // And explicit updates between them, which are needed
697    // for the versioned type
698    impl From<ExampleV2> for ExampleV4 {
699        fn from(value: ExampleV2) -> Self {
700            Self { the_value: value }
701        }
702    }
703
704    impl From<ExampleV3> for ExampleV4 {
705        fn from(value: ExampleV3) -> Self {
706            Self { the_value: value.0 }
707        }
708    }
709
710    #[test]
711    pub fn updates_to_latest_work() {
712        let expected_latest = ExampleV4::of(5);
713        let v1: ExampleV1 = 5;
714        validate_latest(v1, expected_latest.clone());
715        let v2: ExampleV2 = 5;
716        validate_latest(v2, expected_latest.clone());
717        let v3 = ExampleV3(5);
718        validate_latest(v3, expected_latest.clone());
719        let v4 = ExampleV4::of(5);
720        validate_latest(v4, expected_latest);
721    }
722
723    fn validate_latest(
724        actual: impl Into<VersionedExample>,
725        expected: <VersionedExample as Versioned>::LatestVersion,
726    ) {
727        let versioned_actual = actual.into().fully_update();
728        let versioned_expected = VersionedExample::from(expected.clone());
729        // Check fully_update (which returns a VersionedExample)
730        assert_eq!(versioned_actual, versioned_expected,);
731        // Check fully_update_and_into_latest_version (which returns an ExampleV4)
732        assert_eq!(
733            versioned_actual.fully_update_and_into_latest_version(),
734            expected,
735        );
736    }
737
738    #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
739    struct GenericModelV1<T>(T);
740
741    define_single_versioned!(
742        /// This is some rust doc as an example annotation
743        #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
744        VersionedGenericModel(GenericModelVersions)<T> => GenericModel<T> = GenericModelV1<T>
745    );
746
747    #[test]
748    pub fn generated_single_versioned_works() {
749        let v1_model: GenericModel<_> = GenericModelV1(51u64);
750        let versioned = VersionedGenericModel::from(v1_model.clone());
751        let versioned_2 = v1_model.clone().into_versioned();
752        assert_eq!(
753            versioned.clone().fully_update_and_into_latest_version(),
754            v1_model
755        );
756        assert_eq!(versioned, versioned_2);
757    }
758
759    #[test]
760    pub fn verify_sbor_equivalence() {
761        // Value model
762        let v1_model: GenericModel<_> = GenericModelV1(51u64);
763        let versions = GenericModelVersions::V1(v1_model.clone());
764        let versioned = VersionedGenericModel::from(v1_model.clone());
765        let expected_sbor_value = BasicEnumVariantValue {
766            // GenericModelVersions
767            discriminator: 0, // V1 maps to 0 for legacy compatibility
768            fields: vec![
769                // GenericModelV1
770                Value::Tuple {
771                    fields: vec![Value::U64 { value: 51 }],
772                },
773            ],
774        };
775        let encoded_versioned = basic_encode(&versioned).unwrap();
776        let encoded_versions = basic_encode(&versions).unwrap();
777        let expected = basic_encode(&expected_sbor_value).unwrap();
778        assert_eq!(encoded_versioned, expected);
779        assert_eq!(encoded_versions, expected);
780
781        // Type model
782        check_identical_types::<VersionedGenericModel<u64>, GenericModelVersions<u64>>(Some(
783            "VersionedGenericModel",
784        ));
785    }
786
787    fn check_identical_types<T1: Describe<NoCustomTypeKind>, T2: Describe<NoCustomTypeKind>>(
788        name: Option<&'static str>,
789    ) {
790        let (type_id1, schema1) = generate_full_schema_from_single_type::<T1, NoCustomSchema>();
791        let (type_id2, schema2) = generate_full_schema_from_single_type::<T2, NoCustomSchema>();
792
793        assert_eq!(
794            schema1.v1().resolve_type_kind(type_id1),
795            schema2.v1().resolve_type_kind(type_id2)
796        );
797        assert_eq!(
798            schema1
799                .v1()
800                .resolve_type_metadata(type_id1)
801                .unwrap()
802                .clone(),
803            schema2
804                .v1()
805                .resolve_type_metadata(type_id2)
806                .unwrap()
807                .clone()
808                .with_name(name.map(Cow::Borrowed))
809        );
810        assert_eq!(
811            schema1.v1().resolve_type_validation(type_id1),
812            schema2.v1().resolve_type_validation(type_id2)
813        );
814    }
815}