radix_engine/blueprints/models/
keys.rs

1use crate::internal_prelude::*;
2
3macro_rules! declare_key_new_type {
4    // First - explicitly support SortedIndex
5    (
6        content_trait: SortedIndexKeyContentSource,
7        payload_trait: SortedIndexKeyPayload,
8        full_key_content: {
9            full_content_type: $full_content_type:ty,
10            sort_prefix_property_name: $sort_prefix_property_name:ident
11            $(,)?
12        },
13        ----
14        $(#[$attributes:meta])*
15        $vis:vis struct $payload_type_name:ident
16            $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
17            ($content_type:ty)$(;)?
18    ) => {
19        $(#[$attributes])*
20        /// This type represents the payload of the key of a particular sorted index collection.
21        $vis struct $payload_type_name
22            $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)?
23            {
24                pub $sort_prefix_property_name: u16,
25                pub content: $content_type,
26            }
27
28        impl $payload_type_name {
29            pub fn new($sort_prefix_property_name: u16, content: $content_type) -> Self {
30                Self {
31                    $sort_prefix_property_name,
32                    content,
33                }
34            }
35
36            pub fn sort_prefix(&self) -> u16 {
37                self.$sort_prefix_property_name
38            }
39        }
40
41        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
42            core::convert::AsRef<$content_type>
43            for $payload_type_name $(< $( $lt ),+ >)?
44        {
45            fn as_ref(&self) -> &$content_type {
46                &self.content
47            }
48        }
49
50        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
51            core::convert::AsMut<$content_type>
52            for $payload_type_name $(< $( $lt ),+ >)?
53        {
54            fn as_mut(&mut self) -> &mut $content_type {
55                &mut self.content
56            }
57        }
58
59        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
60            SortedIndexKeyPayload
61            for $payload_type_name $(< $( $lt ),+ >)?
62        {
63            type Content = $content_type;
64            type FullContent = $full_content_type;
65
66            fn into_sort_key_and_content(self) -> (u16, Self::Content) {
67                (self.sort_prefix(), self.content)
68            }
69
70            fn as_sort_key_and_content(&self) -> (u16, &Self::Content) {
71                (self.sort_prefix(), &self.content)
72            }
73
74            fn from_sort_key_and_content(sort_prefix: u16, content: Self::Content) -> Self {
75                Self {
76                    $sort_prefix_property_name: sort_prefix,
77                    content,
78                }
79            }
80        }
81
82        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
83            TryFrom<&SubstateKey>
84            for $payload_type_name $(< $( $lt ),+ >)?
85        {
86            type Error = ();
87
88            fn try_from(substate_key: &SubstateKey) -> Result<Self, Self::Error> {
89                let (sort_prefix, payload_bytes) = substate_key.for_sorted().ok_or(())?;
90                let content = scrypto_decode(payload_bytes).map_err(|_| ())?;
91                Ok(Self::from_sort_key_and_content(u16::from_be_bytes(*sort_prefix), content))
92            }
93        }
94
95        // Note - we assume that both:
96        // * SortedIndexKeyContentSource<_Payload>
97        // * SortedIndexKeyFullContent<_Payload>
98        // are already/manually implemented for $content_type
99    };
100    (
101        content_trait: SortedIndexKeyContentSource,
102        payload_trait: SortedIndexKeyPayload,
103        ----
104        $(#[$attributes:meta])*
105        $vis:vis struct $payload_type_name:ident
106            $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
107            ($content_type:ty)$(;)?
108    ) => {
109        compile_error!(
110            "Make sure to add a `full_key_content: { full_content_type: <ident>, sort_prefix_property_name: <..>}` property after the `key_type` property for SortedIndex collection definitions"
111        )
112    };
113    (
114        content_trait: $content_trait:ident,
115        payload_trait: $payload_trait:ident,
116        full_key_content: $full_key_content:tt,
117        ----
118        $(#[$attributes:meta])*
119        $vis:vis struct $payload_type_name:ident
120            $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
121            ($content_type:ty)$(;)?
122    ) => {
123        compile_error!(
124            "Only add a `full_key_content` property for SortedIndex collection definitions"
125        )
126    };
127    // Then - support the other collection types
128    (
129        content_trait: $content_trait:ident,
130        payload_trait: $payload_trait:ident,
131        ----
132        $(#[$attributes:meta])*
133        $vis:vis struct $payload_type_name:ident
134        $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)?
135        ($content_type:ty)$(;)?
136    ) => {
137        $(#[$attributes])*
138        #[sbor(transparent, transparent_name)]
139        /// This new type represents the payload of the key of a particular collection.
140        $vis struct $payload_type_name
141            $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)?
142            {
143                pub content: $content_type,
144            }
145
146        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
147            core::convert::From<$content_type>
148            for $payload_type_name $(< $( $lt ),+ >)?
149        {
150            fn from(value: $content_type) -> Self {
151                Self {
152                    content: value,
153                }
154            }
155        }
156
157        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
158            core::convert::AsRef<$content_type>
159            for $payload_type_name $(< $( $lt ),+ >)?
160        {
161            fn as_ref(&self) -> &$content_type {
162                &self.content
163            }
164        }
165
166        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
167            core::convert::AsMut<$content_type>
168            for $payload_type_name $(< $( $lt ),+ >)?
169        {
170            fn as_mut(&mut self) -> &mut $content_type {
171                &mut self.content
172            }
173        }
174
175        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
176            TryFrom<&SubstateKey>
177            for $payload_type_name $(< $( $lt ),+ >)?
178        {
179            type Error = ();
180
181            fn try_from(substate_key: &SubstateKey) -> Result<Self, Self::Error> {
182                let key = substate_key.for_map().ok_or(())?;
183                scrypto_decode::<Self>(&key).map_err(|_| ())
184            }
185        }
186
187        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
188            $payload_trait
189            for $payload_type_name $(< $( $lt ),+ >)?
190        {
191            type Content = $content_type;
192
193            fn into_content(self) -> Self::Content {
194                self.content
195            }
196        }
197
198        impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
199            $content_trait<$payload_type_name$(< $( $lt ),+ >)?>
200            for $content_type
201        {
202            fn into_content(self) -> $content_type {
203                self
204            }
205        }
206    }
207}
208#[allow(unused)]
209pub(crate) use declare_key_new_type;
210
211/// This trait is intended to be implemented by an explicit new type for for the given
212/// `{ content: T }` key of a particular key value collection.
213pub trait KeyValueKeyPayload:
214    Sized + AsRef<Self::Content> + AsMut<Self::Content> + From<Self::Content>
215{
216    type Content: KeyValueKeyContentSource<Self>;
217
218    fn into_content(self) -> Self::Content;
219    fn from_content(inner_content: Self::Content) -> Self {
220        Self::from(inner_content)
221    }
222
223    fn from_content_source<T: KeyValueKeyContentSource<Self>>(content: T) -> Self {
224        Self::from_content(content.into_content())
225    }
226}
227
228/// This trait is intended to be implemented by types which embody the content
229/// of a key for a particular key value collection.
230///
231/// Note:
232/// * Multiple types might be mappable into the key payload, and so implement this trait
233/// * This trait is only one way - from value into content
234/// * This trait uses a generic, because the same type might be usable as a key for multiple
235///   substates
236pub trait KeyValueKeyContentSource<Payload: KeyValueKeyPayload>: Sized {
237    fn into_content(self) -> Payload::Content;
238
239    fn into_key(self) -> Payload {
240        Payload::from_content_source(self)
241    }
242}
243
244/// This trait is intended to be implemented by an explicit new type for the given
245/// `{ content: T }` key of a particular index collection.
246pub trait IndexKeyPayload:
247    Sized + AsRef<Self::Content> + AsMut<Self::Content> + From<Self::Content>
248{
249    type Content: IndexKeyContentSource<Self>;
250
251    fn into_content(self) -> Self::Content;
252    fn from_content(content: Self::Content) -> Self {
253        Self::from(content)
254    }
255
256    fn from_content_source<T: IndexKeyContentSource<Self>>(content: T) -> Self {
257        Self::from_content(content.into_content())
258    }
259}
260
261/// This trait is intended to be implemented by types which embody the content
262/// of a key for a particular index collection.
263///
264/// Note:
265/// * Multiple types might be mappable into the key payload, and so implement this trait
266/// * This trait is only one way - from value into content
267/// * This trait uses a generic, because the same type might be usable as a key for multiple
268///   substates
269pub trait IndexKeyContentSource<Payload: IndexKeyPayload>: Sized {
270    fn into_content(self) -> Payload::Content;
271
272    fn into_key(self) -> Payload {
273        Payload::from_content_source(self)
274    }
275}
276
277/// This trait is intended to be implemented by an explicit new type for the given
278/// `{ sort_index: u16, content: T }` key for a particular sorted index collection.
279pub trait SortedIndexKeyPayload: Sized + AsRef<Self::Content> + AsMut<Self::Content> {
280    type Content;
281    type FullContent: SortedIndexKeyFullContent<Self>;
282
283    fn from_sort_key_and_content(sort_key: u16, content: Self::Content) -> Self;
284    fn into_sort_key_and_content(self) -> (u16, Self::Content);
285    fn as_sort_key_and_content(&self) -> (u16, &Self::Content);
286
287    fn into_full_content(self) -> Self::FullContent {
288        let (sort_key, content) = self.into_sort_key_and_content();
289        Self::FullContent::from_sort_key_and_content(sort_key, content)
290    }
291
292    fn from_content_source<T: SortedIndexKeyContentSource<Self>>(content: T) -> Self {
293        let (sort_key, content) = content.into_sort_key_and_content();
294        Self::from_sort_key_and_content(sort_key, content)
295    }
296}
297
298/// This trait is intended to be implemented by types which embody the content
299/// of a key for a particular sorted index collection.
300///
301/// Note:
302/// * Multiple types might be mappable into the key payload, and so implement this trait
303/// * This trait is only one way - from value into content
304/// * This trait uses a generic, because the same type might be usable as a key for multiple
305///   explicit substates
306pub trait SortedIndexKeyContentSource<Payload: SortedIndexKeyPayload>: Sized {
307    fn sort_key(&self) -> u16;
308    fn into_content(self) -> Payload::Content;
309
310    fn into_sort_key_and_content(self) -> (u16, Payload::Content) {
311        (self.sort_key(), self.into_content())
312    }
313
314    fn into_key(self) -> Payload {
315        Payload::from_content_source(self)
316    }
317}
318
319/// This trait is intended to be implemented by the canonical content
320/// of a key for a particular sorted index collection.
321pub trait SortedIndexKeyFullContent<Payload: SortedIndexKeyPayload>:
322    SortedIndexKeyContentSource<Payload>
323{
324    fn from_sort_key_and_content(sort_key: u16, content: Payload::Content) -> Self;
325    fn as_content(&self) -> &Payload::Content;
326
327    fn as_sort_key_and_content(&self) -> (u16, &Payload::Content) {
328        (self.sort_key(), self.as_content())
329    }
330}