sbor/
enum_variant.rs

1use crate::*;
2
3pub struct SborFixedEnumVariant<const DISCRIMINATOR: u8, T> {
4    pub fields: T,
5}
6
7impl<const DISCRIMINATOR: u8, T> SborFixedEnumVariant<DISCRIMINATOR, T> {
8    pub fn new(fields: T) -> Self {
9        Self { fields }
10    }
11
12    pub fn discriminator() -> u8 {
13        DISCRIMINATOR
14    }
15
16    pub fn for_encoding(fields: &T) -> SborFixedEnumVariant<DISCRIMINATOR, &T> {
17        SborFixedEnumVariant { fields }
18    }
19
20    pub fn into_fields(self) -> T {
21        self.fields
22    }
23}
24
25impl<X: CustomValueKind, const DISCRIMINATOR: u8, T: SborTuple<X>> Categorize<X>
26    for SborFixedEnumVariant<DISCRIMINATOR, T>
27{
28    fn value_kind() -> ValueKind<X> {
29        ValueKind::Enum
30    }
31}
32
33impl<X: CustomValueKind, const DISCRIMINATOR: u8, T: SborTuple<X>> SborEnum<X>
34    for SborFixedEnumVariant<DISCRIMINATOR, T>
35{
36    fn get_length(&self) -> usize {
37        self.fields.get_length()
38    }
39
40    fn get_discriminator(&self) -> u8 {
41        DISCRIMINATOR
42    }
43}
44
45impl<
46        X: CustomValueKind,
47        E: Encoder<X>,
48        const DISCRIMINATOR: u8,
49        T: Encode<X, E> + SborTuple<X>,
50    > Encode<X, E> for SborFixedEnumVariant<DISCRIMINATOR, T>
51{
52    fn encode_value_kind(&self, encoder: &mut E) -> Result<(), EncodeError> {
53        encoder.write_value_kind(Self::value_kind())
54    }
55
56    fn encode_body(&self, encoder: &mut E) -> Result<(), EncodeError> {
57        encoder.write_discriminator(DISCRIMINATOR)?;
58        self.fields.encode_body(encoder)
59    }
60}
61
62impl<
63        X: CustomValueKind,
64        D: Decoder<X>,
65        const DISCRIMINATOR: u8,
66        T: Decode<X, D> + SborTuple<X>,
67    > Decode<X, D> for SborFixedEnumVariant<DISCRIMINATOR, T>
68{
69    #[inline]
70    fn decode_body_with_value_kind(
71        decoder: &mut D,
72        value_kind: ValueKind<X>,
73    ) -> Result<Self, DecodeError> {
74        decoder.check_preloaded_value_kind(value_kind, Self::value_kind())?;
75        decoder.read_expected_discriminator(DISCRIMINATOR)?;
76        // The fields is actually a tuple type - so we pass in ValueKind::Tuple to trick the encoding
77        let fields = T::decode_body_with_value_kind(decoder, ValueKind::Tuple)?;
78        Ok(Self { fields })
79    }
80}
81
82//=======================================================================================
83// Now define a trait `IsSborFixedEnumVariant<T>` - this is intended to represent
84//  `SborFixedEnumVariant<?, T>` of unknown discriminator.
85// This is only really needed because of https://github.com/rust-lang/rust/issues/76560
86//  and the explanation below "Why is this required as an associated type?".
87// In particular, see eg `TransactionPayload` where we couldn't define `SborFixedEnumVariant<{ Self::DISCRIMINATOR }, X>`
88//=======================================================================================
89pub trait IsSborFixedEnumVariant<F> {
90    const DISCRIMINATOR: u8;
91    fn new(fields: F) -> Self;
92    fn into_fields(self) -> F;
93}
94
95impl<const DISCRIMINATOR: u8, F> IsSborFixedEnumVariant<F>
96    for SborFixedEnumVariant<DISCRIMINATOR, F>
97{
98    const DISCRIMINATOR: u8 = DISCRIMINATOR;
99
100    fn new(fields: F) -> Self {
101        Self::new(fields)
102    }
103
104    fn into_fields(self) -> F {
105        self.fields
106    }
107}
108
109/// This trait is output for unique unskipped single children of enum variants, when
110/// `#[sbor(impl_variant_traits)]` is specified on an Enum or
111/// `#[sbor(impl_variant_trait)]` is specified on a single Enum variant.
112///
113/// It allows considering this type as representing an enum variant type
114/// under its parent enum. There are two flavours of how this embedding works in SBOR:
115/// * In unflattened variants, it is a variant with fields (Self,)
116/// * In flattened variants (only possible for tuple types) it is a variant with fields Self
117///
118/// This trait pairs well with the `#[sbor(flatten)]` attribute, for implementing
119/// the "enum variant is singleton struct type" pattern, which allows a number of benefits:
120/// * A function can take or return a particular variant
121/// * If code size is important (e.g. when building Scrypto), we want to enable pruning of
122///   any serialization code we don't need. Using `as_encodable_variant` and `from_decoded_variant`
123///   avoids pulling in the parent `TEnum` serialization code; and the serialization code for any
124///   unused types in other discriminators
125///
126/// ### Note on generic parameter ordering
127/// On this trait, we do not put `X` first, as is normal with the SBOR traits.
128///
129/// Instead, `TEnum` comes before `X` so that the trait can be implemented on any foreign
130/// type assuming `TEnum` is local. This is so that the cryptic orphan rule
131/// discussed in this stack overflow comment is satisfied https://stackoverflow.com/a/63131661
132///
133/// With this ordering we have P0 = X, T0 = Child Type (possibly foreign), T1 = TEnum, T2 = X,
134/// which passes the check.
135pub trait SborEnumVariantFor<TEnum: SborEnum<X>, X: CustomValueKind> {
136    const DISCRIMINATOR: u8;
137    const IS_FLATTENED: bool;
138
139    /// VariantFields is either `Self` if `IS_FLATTENED` else is `(Self,)`
140    type VariantFields: SborTuple<X>;
141    fn from_variant_fields(variant_fields: Self::VariantFields) -> Self;
142
143    /// VariantFieldsRef is either `&Self` if `IS_FLATTENED` else is `(&Self,)`
144    type VariantFieldsRef<'a>: SborTuple<X>
145    where
146        Self: 'a,
147        Self::VariantFields: 'a;
148    fn as_variant_fields_ref(&self) -> Self::VariantFieldsRef<'_>;
149
150    /// This should always be `SborFixedEnumVariant<{ [DISCRIMINATOR] as u8 }, Self::VariantFields>`
151    ///
152    /// ### Why is this required as an associated type?
153    /// Ideally we'd not need this and just have `as_encodable_variant` return
154    /// `SborFixedEnumVariant<{ Self::DISCRIMINATOR as u8 }, Self::VariantFieldsRef<'_>>`
155    /// But this gets "error: generic parameters may not be used in const operations"
156    ///
157    /// ### Why doesn't this require `VecDecode<X>`?
158    /// We don't want a compiler error if a type only implements Categorize (and so gets this trait)
159    /// but not Decode. Really I'd like to say `OwnedVariant: VecDecode<X> if Self: VecDecode<X>`
160    /// but Rust doesn't support that. Instead, users will need to add the bound on the associated
161    /// type themselves.
162    type OwnedVariant: IsSborFixedEnumVariant<Self::VariantFields>;
163
164    /// Should always be `SborFixedEnumVariant<{ [DISCRIMINATOR] as u8 }, &'a Self::VariantFields>`
165    ///
166    /// ### Why is this required as an associated type?
167    /// Ideally we'd not need this and just have `as_encodable_variant` return
168    /// `SborFixedEnumVariant<{ Self::DISCRIMINATOR }, Self::VariantFields>`
169    /// But this gets "error: generic parameters may not be used in const operations" which needs
170    /// the `const-generics` feature which has been in progress for a number of years.
171    /// See https://github.com/rust-lang/project-const-generics/issues/31
172    ///
173    /// ### Why doesn't this require `VecEncode<X>`?
174    /// We don't want a compiler error if a type only implements Categorize (and so gets this trait)
175    /// but not Encode. Really I'd like to say `BorrowedVariant<'a>: VecEncode<X> if Self: VecEncode<X>`
176    /// but Rust doesn't support that. Instead, users will need to add the bound on the associated
177    /// type themselves.
178    ///
179    /// Instead, you can express a trait bound as such:
180    /// ```ignore
181    /// pub trait MyNewSuperTrait:
182    ///     for<'a> SborEnumVariantFor<
183    ///        TEnum,
184    ///        X,
185    ///        OwnedVariant: ManifestDecode,
186    ///        BorrowedVariant<'a>: ManifestEncode,
187    ///     >
188    /// {}
189    /// ```
190    type BorrowedVariant<'a>: IsSborFixedEnumVariant<Self::VariantFieldsRef<'a>>
191    where
192        Self: 'a,
193        Self::VariantFields: 'a;
194
195    /// Can be used to encode the type as a variant under `TEnum`, like this:
196    /// `encoder.encode(x.as_encodable_variant())`.
197    ///
198    /// To use this pattern in a generic context, you will likely need to add a bound like
199    /// `for<'a> T::BorrowedVariant<'a>: VecEncode<X>`.
200    fn as_encodable_variant<'a>(&'a self) -> Self::BorrowedVariant<'a> {
201        Self::BorrowedVariant::new(self.as_variant_fields_ref())
202    }
203
204    /// Can be used to decode the type from an encoded variant, like this:
205    /// `T::from_decoded_variant(decoder.decode()?)`.
206    ///
207    /// To use this pattern in a generic context, you will likely need to add a bound like
208    /// `T::OwnedVariant: VecDecode<X>`.
209    fn from_decoded_variant(variant: Self::OwnedVariant) -> Self
210    where
211        Self: core::marker::Sized,
212    {
213        Self::from_variant_fields(variant.into_fields())
214    }
215
216    fn into_enum(self) -> TEnum;
217}