scrypto_bindgen/
schema.rs1use radix_blueprint_schema_init::*;
2use radix_common::prelude::*;
3use radix_engine_interface::blueprints::package::*;
4use std::collections::BTreeMap;
5use std::fmt::{Debug, Display};
6
7pub trait PackageSchemaResolver {
8 fn lookup_schema(&self, schema_hash: &SchemaHash) -> Option<Rc<VersionedScryptoSchema>>;
9
10 fn resolve_type_kind(
11 &self,
12 type_identifier: &ScopedTypeId,
13 ) -> Result<LocalTypeKind<ScryptoCustomSchema>, SchemaError>;
14
15 fn resolve_type_metadata(
16 &self,
17 type_identifier: &ScopedTypeId,
18 ) -> Result<TypeMetadata, SchemaError>;
19
20 fn resolve_type_validation(
21 &self,
22 type_identifier: &ScopedTypeId,
23 ) -> Result<TypeValidation<ScryptoCustomTypeValidation>, SchemaError>;
24
25 fn package_address(&self) -> PackageAddress;
26}
27
28pub fn package_interface_from_package_definition<S>(
29 package_definition: BTreeMap<BlueprintVersionKey, BlueprintDefinition>,
30 schema_resolver: &S,
31) -> Result<PackageInterface, SchemaError>
32where
33 S: PackageSchemaResolver,
34{
35 let mut package_interface = PackageInterface::default();
36
37 for (blueprint_key, blueprint_definition) in package_definition.into_iter() {
38 let blueprint_name = blueprint_key.blueprint;
39
40 if let Some((_, fields)) = blueprint_definition.interface.state.fields {
41 for field in fields {
42 if let BlueprintPayloadDef::Static(scoped_type_id) = field.field {
43 package_interface.auxiliary_types.insert(scoped_type_id);
44 }
45 }
46 }
47
48 let functions = &mut package_interface
49 .blueprints
50 .entry(blueprint_name)
51 .or_default()
52 .functions;
53
54 for (function_name, function_schema) in blueprint_definition.interface.functions {
55 let BlueprintPayloadDef::Static(input_type_identifier) = &function_schema.input else {
56 Err(SchemaError::GenericTypeRefsNotSupported)?
57 };
58
59 let inputs_scoped_type_id = {
61 let type_kind = schema_resolver.resolve_type_kind(input_type_identifier)?;
62 if let TypeKind::Tuple { field_types } = type_kind {
63 Ok(field_types
64 .into_iter()
65 .map(|local_type_id| ScopedTypeId(input_type_identifier.0, local_type_id))
66 .collect::<Vec<_>>())
67 } else {
68 Err(SchemaError::FunctionInputIsNotATuple(
69 *input_type_identifier,
70 ))
71 }
72 }?;
73
74 let inputs_field_names = {
76 let type_metadata = schema_resolver.resolve_type_metadata(input_type_identifier)?;
77 match type_metadata.child_names.as_ref() {
78 Some(ChildNames::NamedFields(field_names)) => field_names
80 .iter()
81 .map(|entry| entry.as_ref().to_owned())
82 .collect::<Vec<_>>(),
83 Some(ChildNames::EnumVariants(..)) => {
85 panic!(
86 "We have checked that this is a Tuple and it can't have enum variants."
87 )
88 }
89 None => (0..inputs_scoped_type_id.len())
91 .map(|i| format!("arg{i}"))
92 .collect::<Vec<_>>(),
93 }
94 };
95
96 let BlueprintPayloadDef::Static(output_local_type_index) = &function_schema.output
98 else {
99 return Err(SchemaError::GenericTypeRefsNotSupported);
100 };
101
102 for input_type in inputs_scoped_type_id.iter() {
106 get_scoped_type_ids_in_path(
107 input_type,
108 schema_resolver,
109 &mut package_interface.auxiliary_types,
110 )?;
111 }
112 get_scoped_type_ids_in_path(
113 output_local_type_index,
114 schema_resolver,
115 &mut package_interface.auxiliary_types,
116 )?;
117
118 let function = Function {
119 ident: function_name.to_owned(),
120 receiver: function_schema.receiver.clone(),
121 arguments: inputs_field_names
122 .into_iter()
123 .zip(inputs_scoped_type_id)
124 .collect::<IndexMap<String, ScopedTypeId>>(),
125 returns: *output_local_type_index,
126 };
127 functions.push(function);
128 }
129 }
130
131 Ok(package_interface)
132}
133
134#[derive(Clone, Debug, Default)]
135pub struct PackageInterface {
136 pub blueprints: IndexMap<String, BlueprintInterface>,
139 pub auxiliary_types: HashSet<ScopedTypeId>,
144}
145
146#[derive(Clone, Debug, Default)]
147pub struct BlueprintInterface {
148 pub functions: Vec<Function>,
150}
151
152#[derive(Clone, Debug)]
153pub struct Function {
154 pub ident: String,
155 pub receiver: Option<ReceiverInfo>,
156 pub arguments: IndexMap<String, ScopedTypeId>,
157 pub returns: ScopedTypeId,
158}
159
160fn get_scoped_type_ids_in_path<S>(
161 type_id: &ScopedTypeId,
162 schema_resolver: &S,
163 collection: &mut HashSet<ScopedTypeId>,
164) -> Result<(), SchemaError>
165where
166 S: PackageSchemaResolver,
167{
168 if !collection.insert(*type_id) {
169 return Ok(());
170 }
171
172 let type_kind = schema_resolver.resolve_type_kind(type_id)?;
173
174 match type_kind {
175 TypeKind::Any
176 | TypeKind::Bool
177 | TypeKind::I8
178 | TypeKind::I16
179 | TypeKind::I32
180 | TypeKind::I64
181 | TypeKind::I128
182 | TypeKind::U8
183 | TypeKind::U16
184 | TypeKind::U32
185 | TypeKind::U64
186 | TypeKind::U128
187 | TypeKind::String
188 | TypeKind::Custom(ScryptoCustomTypeKind::Reference)
189 | TypeKind::Custom(ScryptoCustomTypeKind::Own)
190 | TypeKind::Custom(ScryptoCustomTypeKind::Decimal)
191 | TypeKind::Custom(ScryptoCustomTypeKind::PreciseDecimal)
192 | TypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId) => {}
193 TypeKind::Array { element_type } => {
194 let scoped_type_id = ScopedTypeId(type_id.0, element_type);
195 get_scoped_type_ids_in_path(&scoped_type_id, schema_resolver, collection)?;
196 }
197 TypeKind::Tuple { field_types } => {
198 for field_type in field_types {
199 let scoped_type_id = ScopedTypeId(type_id.0, field_type);
200 get_scoped_type_ids_in_path(&scoped_type_id, schema_resolver, collection)?;
201 }
202 }
203 TypeKind::Enum { variants } => {
204 for field_types in variants.values() {
205 for field_type in field_types {
206 let scoped_type_id = ScopedTypeId(type_id.0, *field_type);
207 get_scoped_type_ids_in_path(&scoped_type_id, schema_resolver, collection)?;
208 }
209 }
210 }
211 TypeKind::Map {
212 key_type,
213 value_type,
214 } => {
215 let scoped_type_id = ScopedTypeId(type_id.0, key_type);
216 get_scoped_type_ids_in_path(&scoped_type_id, schema_resolver, collection)?;
217
218 let scoped_type_id = ScopedTypeId(type_id.0, value_type);
219 get_scoped_type_ids_in_path(&scoped_type_id, schema_resolver, collection)?;
220 }
221 }
222
223 Ok(())
224}
225
226#[derive(Clone, Debug)]
227pub enum SchemaError {
228 FunctionInputIsNotATuple(ScopedTypeId),
229 NonExistentLocalTypeIndex(LocalTypeId),
230 FailedToGetSchemaFromSchemaHash,
231 GenericTypeRefsNotSupported,
232 NoNameFound,
233}
234
235impl Display for SchemaError {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 Debug::fmt(&self, f)
238 }
239}