radix_blueprint_schema_init/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(any(feature = "std", feature = "alloc")))]
4compile_error!("Either feature `std` or `alloc` must be enabled for this crate.");
5#[cfg(all(feature = "std", feature = "alloc"))]
6compile_error!("Feature `std` and `alloc` can't be enabled at the same time.");
7
8use bitflags::bitflags;
9use radix_common::prelude::*;
10
11#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
12pub struct KeyValueStoreGenericSubstitutions {
13    pub key_generic_substitution: GenericSubstitution,
14    pub value_generic_substitution: GenericSubstitution,
15    pub allow_ownership: bool, // TODO: Can this be integrated with ScryptoSchema?
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
19pub enum GenericBound {
20    Any,
21}
22
23#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, ScryptoSbor, ManifestSbor)]
24pub enum BlueprintHook {
25    OnVirtualize,
26    OnMove,
27    OnDrop,
28}
29
30/// An initialization object which describes a blueprint's schema including interface, state, and events
31#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
32pub struct BlueprintSchemaInit {
33    /// List of generic parameters which must be provided on component instantiation and the bounds of these generics
34    pub generics: Vec<GenericBound>,
35    /// Sbor schema which describes various types, each identified by a usize
36    pub schema: VersionedScryptoSchema,
37    /// Describes schema of state by mapping fields/collection indices as a generic or directly into the Sbor schema
38    pub state: BlueprintStateSchemaInit,
39    /// Describes schema of events by mapping event names as a generic or directly into the Sbor schema
40    pub events: BlueprintEventSchemaInit,
41    /// Describes schema of types by mapping types names directly into the Sbor schema
42    /// These types are used for external generic substitution
43    pub types: BlueprintTypeSchemaInit,
44    /// Describes interface of function by mapping function names to input/output schema and the code exported function name it maps to
45    pub functions: BlueprintFunctionsSchemaInit,
46    /// Maps hooks to a code exported function name
47    pub hooks: BlueprintHooksInit,
48}
49
50impl Default for BlueprintSchemaInit {
51    fn default() -> Self {
52        Self {
53            generics: Vec::new(),
54            schema: Schema {
55                type_kinds: Vec::new(),
56                type_metadata: Vec::new(),
57                type_validations: Vec::new(),
58            }
59            .into_versioned(),
60            state: BlueprintStateSchemaInit::default(),
61            events: BlueprintEventSchemaInit::default(),
62            types: BlueprintTypeSchemaInit::default(),
63            functions: BlueprintFunctionsSchemaInit::default(),
64            hooks: BlueprintHooksInit::default(),
65        }
66    }
67}
68
69/// Describes the fields and collections some Blueprint has as well
70/// as the schema and properties of each field and collection
71#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
72pub struct BlueprintStateSchemaInit {
73    pub fields: Vec<FieldSchema<TypeRef<LocalTypeId>>>,
74    pub collections: Vec<BlueprintCollectionSchema<TypeRef<LocalTypeId>>>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
78#[sbor(transparent)]
79pub struct BlueprintEventSchemaInit {
80    pub event_schema: IndexMap<String, TypeRef<LocalTypeId>>,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
84#[sbor(transparent)]
85pub struct BlueprintTypeSchemaInit {
86    pub type_schema: IndexMap<String, LocalTypeId>,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
90pub struct FunctionSchemaInit {
91    pub receiver: Option<ReceiverInfo>,
92    pub input: TypeRef<LocalTypeId>,
93    pub output: TypeRef<LocalTypeId>,
94    pub export: String,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
98pub struct BlueprintFunctionsSchemaInit {
99    pub functions: IndexMap<String, FunctionSchemaInit>,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
103pub struct BlueprintHooksInit {
104    // TODO: allow registration of variant count if we make virtualizable entity type fully dynamic
105    pub hooks: IndexMap<BlueprintHook, String>,
106}
107
108impl BlueprintSchemaInit {
109    pub fn exports(&self) -> Vec<String> {
110        self.functions
111            .functions
112            .values()
113            .map(|t| t.export.clone())
114            .chain(self.hooks.hooks.values().cloned())
115            .collect()
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
120pub enum TypeRef<T> {
121    Static(T), // Fully Resolved type is defined in package
122    Generic(u8), // Fully Resolved type is mapped directly to a generic
123               // TODO: How to represent a structure containing a generic?
124}
125
126impl<T> TypeRef<T> {
127    pub fn into_static(self) -> Option<T> {
128        let Self::Static(value) = self else {
129            return None;
130        };
131        Some(value)
132    }
133
134    pub fn assert_static(self) -> T {
135        self.into_static().expect("Must be static")
136    }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
140pub struct BlueprintKeyValueSchema<T> {
141    pub key: T,
142    pub value: T,
143    pub allow_ownership: bool,
144}
145
146impl<T> BlueprintKeyValueSchema<T> {
147    pub fn map<U, F: Fn(T) -> U + Copy>(self, f: F) -> BlueprintKeyValueSchema<U> {
148        BlueprintKeyValueSchema {
149            key: f(self.key),
150            value: f(self.value),
151            allow_ownership: self.allow_ownership,
152        }
153    }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
157pub enum BlueprintCollectionSchema<T> {
158    KeyValueStore(BlueprintKeyValueSchema<T>),
159    Index(BlueprintKeyValueSchema<T>),
160    SortedIndex(BlueprintKeyValueSchema<T>),
161}
162
163impl<T> BlueprintCollectionSchema<T> {
164    pub fn map<U, F: Fn(T) -> U + Copy>(self, f: F) -> BlueprintCollectionSchema<U> {
165        match self {
166            BlueprintCollectionSchema::Index(schema) => {
167                BlueprintCollectionSchema::Index(schema.map(f))
168            }
169            BlueprintCollectionSchema::SortedIndex(schema) => {
170                BlueprintCollectionSchema::SortedIndex(schema.map(f))
171            }
172            BlueprintCollectionSchema::KeyValueStore(schema) => {
173                BlueprintCollectionSchema::KeyValueStore(schema.map(f))
174            }
175        }
176    }
177}
178
179pub trait BlueprintFeature {
180    fn feature_name(&self) -> &'static str;
181}
182
183/// Expresses a condition based on features enabled on a component
184#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
185pub enum Condition {
186    Always,
187    IfFeature(String),
188    IfOuterFeature(String),
189}
190
191impl Condition {
192    pub fn if_feature(feature: impl BlueprintFeature) -> Self {
193        Self::IfFeature(feature.feature_name().into())
194    }
195
196    pub fn if_outer_feature(feature: impl BlueprintFeature) -> Self {
197        Self::IfOuterFeature(feature.feature_name().into())
198    }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
202pub enum FieldTransience {
203    NotTransient,
204    // TODO: Will need to change this Vec<u8> to ScryptoValue to support default values with global references
205    TransientStatic {
206        /// The default value a transient substate will have on first read
207        default_value: Vec<u8>,
208    },
209}
210
211/// Describes a field for a Blueprint
212#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
213pub struct FieldSchema<V> {
214    pub field: V,
215    /// Condition for this field to exist
216    pub condition: Condition,
217    /// Describes if this field should be persisted
218    pub transience: FieldTransience,
219}
220
221impl FieldSchema<TypeRef<LocalTypeId>> {
222    pub fn if_feature<I: Into<LocalTypeId>, S: ToString>(value: I, feature: S) -> Self {
223        FieldSchema {
224            field: TypeRef::Static(value.into()),
225            condition: Condition::IfFeature(feature.to_string()),
226            transience: FieldTransience::NotTransient,
227        }
228    }
229
230    pub fn if_outer_feature<I: Into<LocalTypeId>, S: ToString>(value: I, feature: S) -> Self {
231        FieldSchema {
232            field: TypeRef::Static(value.into()),
233            condition: Condition::IfOuterFeature(feature.to_string()),
234            transience: FieldTransience::NotTransient,
235        }
236    }
237
238    pub fn static_field<I: Into<LocalTypeId>>(value: I) -> Self {
239        FieldSchema {
240            field: TypeRef::Static(value.into()),
241            condition: Condition::Always,
242            transience: FieldTransience::NotTransient,
243        }
244    }
245
246    pub fn transient_field<I: Into<LocalTypeId>, E: ScryptoEncode>(
247        value: I,
248        default_value: E,
249    ) -> Self {
250        FieldSchema {
251            field: TypeRef::Static(value.into()),
252            condition: Condition::Always,
253            transience: FieldTransience::TransientStatic {
254                default_value: scrypto_encode(&default_value).unwrap(),
255            },
256        }
257    }
258}
259
260bitflags! {
261    #[derive(Sbor)]
262    pub struct RefTypes: u32 {
263        const NORMAL = 0b00000001;
264        const DIRECT_ACCESS = 0b00000010;
265    }
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
269pub struct ReceiverInfo {
270    pub receiver: Receiver,
271    pub ref_types: RefTypes,
272}
273
274impl ReceiverInfo {
275    pub fn normal_ref() -> Self {
276        Self {
277            receiver: Receiver::SelfRef,
278            ref_types: RefTypes::NORMAL,
279        }
280    }
281
282    pub fn normal_ref_mut() -> Self {
283        Self {
284            receiver: Receiver::SelfRefMut,
285            ref_types: RefTypes::NORMAL,
286        }
287    }
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
291pub enum Receiver {
292    SelfRef,
293    SelfRefMut,
294}