scrypto_bindgen/
translation.rs

1//! This module converts the models from `schema.rs` to the `ast.rs` models which are eventually
2//! converted to a TokenStream.
3
4use super::types::{BlueprintFunctionSignaturesReplacementMap, FunctionSignaturesReplacementMap};
5use super::{ast, schema};
6use crate::{ident, token_stream_from_str};
7use radix_blueprint_schema_init::*;
8use radix_common::prelude::*;
9
10/// A list of the packages that we generate bindings for. This must match the list in the
11/// update-bindings.sh script.
12const PACKAGES_WITH_BINDINGS: &[PackageAddress] = &[
13    FAUCET_PACKAGE,
14    CONSENSUS_MANAGER_PACKAGE,
15    IDENTITY_PACKAGE,
16    ACCOUNT_PACKAGE,
17    POOL_PACKAGE,
18    ACCESS_CONTROLLER_PACKAGE,
19    LOCKER_PACKAGE,
20];
21
22pub fn package_schema_interface_to_ast_interface<S>(
23    schema_interface: schema::PackageInterface,
24    package_address: PackageAddress,
25    schema_resolver: &S,
26    blueprint_replacement_map: &BlueprintFunctionSignaturesReplacementMap,
27) -> Result<ast::PackageStub, schema::SchemaError>
28where
29    S: schema::PackageSchemaResolver,
30{
31    Ok(ast::PackageStub {
32        blueprints: schema_interface
33            .blueprints
34            .into_iter()
35            .map(|(blueprint_name, blueprint_interface)| {
36                blueprint_schema_interface_to_ast_interface(
37                    blueprint_interface,
38                    package_address,
39                    blueprint_name,
40                    schema_resolver,
41                    blueprint_replacement_map,
42                )
43            })
44            .collect::<Result<_, _>>()?,
45        auxiliary_types: schema_auxiliary_types_to_ast_types(
46            schema_interface.auxiliary_types,
47            schema_resolver,
48        )?,
49    })
50}
51
52pub fn blueprint_schema_interface_to_ast_interface<S>(
53    schema_interface: schema::BlueprintInterface,
54    package_address: PackageAddress,
55    blueprint_name: String,
56    schema_resolver: &S,
57    blueprint_replacement_map: &BlueprintFunctionSignaturesReplacementMap,
58) -> Result<ast::BlueprintStub, schema::SchemaError>
59where
60    S: schema::PackageSchemaResolver,
61{
62    Ok(ast::BlueprintStub {
63        fn_signatures: schema_interface
64            .functions
65            .into_iter()
66            .map(|func| {
67                function_schema_interface_to_ast_interface(
68                    func,
69                    schema_resolver,
70                    blueprint_replacement_map.get(&blueprint_name),
71                )
72            })
73            .collect::<Result<_, _>>()?,
74        blueprint_name,
75        package_address,
76    })
77}
78
79pub fn function_schema_interface_to_ast_interface<S>(
80    schema_interface: schema::Function,
81    schema_resolver: &S,
82    func_sig_replacements_map: Option<&FunctionSignaturesReplacementMap>,
83) -> Result<ast::FnSignature, schema::SchemaError>
84where
85    S: schema::PackageSchemaResolver,
86{
87    let fn_type = match schema_interface.receiver {
88        Some(ReceiverInfo {
89            ref_types: RefTypes::NORMAL | RefTypes::DIRECT_ACCESS,
90            receiver: Receiver::SelfRef,
91        }) => ast::FnType::Method {
92            is_mutable_receiver: false,
93        },
94        Some(ReceiverInfo {
95            ref_types: RefTypes::NORMAL | RefTypes::DIRECT_ACCESS,
96            receiver: Receiver::SelfRefMut,
97        }) => ast::FnType::Method {
98            is_mutable_receiver: true,
99        },
100        None => ast::FnType::Function,
101        _ => panic!("Invalid BitFlags for RefTypes"),
102    };
103    let function_ident = ident!(&schema_interface.ident);
104
105    // Check if there are some replacements for particular method name
106    let func_sig_replacements = func_sig_replacements_map
107        .and_then(|replacements_map| replacements_map.get(&schema_interface.ident));
108
109    let inputs = schema_interface
110        .arguments
111        .into_iter()
112        .enumerate()
113        .map(|(idx, (arg_name, arg_type_index))| {
114            // Get type replacement if exists for argument idx
115            let ty = func_sig_replacements.and_then(|func| func.arg.get(&idx));
116
117            let ty_name = match ty {
118                Some(ty) => ty.clone(),
119                None => type_name(&arg_type_index, schema_resolver)?,
120            };
121            Ok((ident!(&arg_name), token_stream_from_str!(&ty_name)))
122        })
123        .collect::<Result<_, _>>()?;
124
125    // Get type replacement if exists for return type
126    let ty = func_sig_replacements.and_then(|func| func.output.clone());
127    let ty_name = match ty {
128        Some(ty) => ty,
129        None => type_name(&schema_interface.returns, schema_resolver)?,
130    };
131    let output = token_stream_from_str!(&ty_name);
132
133    Ok(ast::FnSignature {
134        inputs,
135        fn_type,
136        ident: function_ident,
137        output,
138    })
139}
140
141fn type_name<S>(
142    type_identifier: &ScopedTypeId,
143    schema_resolver: &S,
144) -> Result<String, schema::SchemaError>
145where
146    S: schema::PackageSchemaResolver,
147{
148    let type_kind = schema_resolver.resolve_type_kind(type_identifier)?;
149    let type_metadata = schema_resolver.resolve_type_metadata(type_identifier)?;
150    let type_validation = schema_resolver.resolve_type_validation(type_identifier)?;
151    let metadata_type_name = type_metadata.get_name_string();
152
153    let name = match type_kind {
154        TypeKind::Any => metadata_type_name.unwrap_or("ScryptoValue".to_owned()),
155        TypeKind::Bool => metadata_type_name.unwrap_or("bool".to_owned()),
156        TypeKind::I8 => metadata_type_name.unwrap_or("i8".to_owned()),
157        TypeKind::I16 => metadata_type_name.unwrap_or("i16".to_owned()),
158        TypeKind::I32 => metadata_type_name.unwrap_or("i32".to_owned()),
159        TypeKind::I64 => metadata_type_name.unwrap_or("i64".to_owned()),
160        TypeKind::I128 => metadata_type_name.unwrap_or("i128".to_owned()),
161        TypeKind::U8 => metadata_type_name.unwrap_or("u8".to_owned()),
162        TypeKind::U16 => metadata_type_name.unwrap_or("u16".to_owned()),
163        TypeKind::U32 => metadata_type_name.unwrap_or("u32".to_owned()),
164        TypeKind::U64 => metadata_type_name.unwrap_or("u64".to_owned()),
165        TypeKind::U128 => metadata_type_name.unwrap_or("u128".to_owned()),
166        TypeKind::String => metadata_type_name.unwrap_or("String".to_owned()),
167        TypeKind::Array { element_type } => metadata_type_name.unwrap_or(format!(
168            "Vec<{}>",
169            type_name(
170                &ScopedTypeId(type_identifier.0, element_type),
171                schema_resolver,
172            )?
173        )),
174        TypeKind::Tuple { field_types } => {
175            metadata_type_name.unwrap_or(match field_types.as_slice() {
176                [] => "()".to_owned(),
177                types => format!(
178                    "({},)",
179                    types
180                        .iter()
181                        .map(|local_type_index| type_name(
182                            &ScopedTypeId(type_identifier.0, *local_type_index),
183                            schema_resolver,
184                        ))
185                        .collect::<Result<Vec<String>, _>>()?
186                        .join(", ")
187                ),
188            })
189        }
190        TypeKind::Enum { variants } => {
191            // There is currently no way to know if this type has generics or not. Thus, we need to
192            // deal with generic enums from the standard library in a special way. We determine if
193            // the type at hand is `Option` if: the name in the metadata is "Option" and the local
194            // type index is of a well known type.
195            match (
196                type_metadata.get_name(),
197                variants.len(),
198                variants.get(&0).as_ref().map(|vec| vec.as_slice()),
199                variants.get(&1).as_ref().map(|vec| vec.as_slice()),
200            ) {
201                (Some("Option"), 2usize, Some([]), Some([some_type_index])) => Ok(format!(
202                    "Option<{}>",
203                    type_name(
204                        &ScopedTypeId(type_identifier.0, *some_type_index),
205                        schema_resolver,
206                    )?
207                )),
208                (Some("Result"), 2usize, Some([ok_type_index]), Some([err_type_index])) => {
209                    Ok(format!(
210                        "Result<{}, {}>",
211                        type_name(
212                            &ScopedTypeId(type_identifier.0, *ok_type_index),
213                            schema_resolver,
214                        )?,
215                        type_name(
216                            &ScopedTypeId(type_identifier.0, *err_type_index),
217                            schema_resolver,
218                        )?
219                    ))
220                }
221                (Some(name), ..) => Ok(name.to_owned()),
222                (None, ..) => Err(schema::SchemaError::NoNameFound),
223            }?
224        }
225        TypeKind::Map {
226            key_type,
227            value_type,
228        } => metadata_type_name.unwrap_or(format!(
229            "IndexMap<{}, {}>",
230            type_name(&ScopedTypeId(type_identifier.0, key_type), schema_resolver,)?,
231            type_name(
232                &ScopedTypeId(type_identifier.0, value_type),
233                schema_resolver,
234            )?
235        )),
236        TypeKind::Custom(custom_type_kind) => match custom_type_kind {
237            ScryptoCustomTypeKind::Reference => match type_validation {
238                TypeValidation::None => metadata_type_name.unwrap_or("Reference".to_owned()),
239                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
240                    reference_type_validation,
241                )) => match reference_type_validation {
242                    ReferenceValidation::IsGlobal => "GlobalAddress".to_owned(),
243                    ReferenceValidation::IsGlobalPackage => "PackageAddress".to_owned(),
244                    ReferenceValidation::IsGlobalComponent => "ComponentAddress".to_owned(),
245                    ReferenceValidation::IsGlobalResourceManager => "ResourceAddress".to_owned(),
246                    ReferenceValidation::IsGlobalTyped(package_address, blueprint_name) => {
247                        let this_package_address = schema_resolver.package_address();
248                        if package_address.is_none()
249                            || PACKAGES_WITH_BINDINGS.contains(&this_package_address)
250                            || package_address.is_some_and(|package_address| {
251                                this_package_address == package_address
252                            })
253                        {
254                            format!("Global<{}>", blueprint_name)
255                        } else {
256                            metadata_type_name.unwrap_or("Reference".to_owned())
257                        }
258                    }
259                    ReferenceValidation::IsInternal
260                    | ReferenceValidation::IsInternalTyped(_, _) => "InternalAddress".to_owned(),
261                },
262                TypeValidation::I8(_)
263                | TypeValidation::I16(_)
264                | TypeValidation::I32(_)
265                | TypeValidation::I64(_)
266                | TypeValidation::I128(_)
267                | TypeValidation::U8(_)
268                | TypeValidation::U16(_)
269                | TypeValidation::U32(_)
270                | TypeValidation::U64(_)
271                | TypeValidation::U128(_)
272                | TypeValidation::String(_)
273                | TypeValidation::Array(_)
274                | TypeValidation::Map(_)
275                | TypeValidation::Custom(ScryptoCustomTypeValidation::Own(..)) => {
276                    panic!("Unexpected state: a reference type with non-reference validation.")
277                }
278            },
279            ScryptoCustomTypeKind::Own => match type_validation {
280                TypeValidation::None => metadata_type_name.unwrap_or("Own".to_owned()),
281                TypeValidation::Custom(ScryptoCustomTypeValidation::Own(own_type_validation)) => {
282                    match own_type_validation {
283                        OwnValidation::IsBucket => {
284                            metadata_type_name.unwrap_or("Bucket".to_owned())
285                        }
286                        OwnValidation::IsProof => metadata_type_name.unwrap_or("Proof".to_owned()),
287                        OwnValidation::IsVault => metadata_type_name.unwrap_or("Vault".to_owned()),
288                        OwnValidation::IsKeyValueStore => "Own".to_owned(),
289                        OwnValidation::IsGlobalAddressReservation => {
290                            metadata_type_name.unwrap_or("GlobalAddressReservation".to_owned())
291                        }
292                        OwnValidation::IsTypedObject(package_address, blueprint_name) => {
293                            let this_package_address = schema_resolver.package_address();
294                            if package_address.is_none()
295                                || PACKAGES_WITH_BINDINGS.contains(&this_package_address)
296                                || package_address.is_some_and(|package_address| {
297                                    this_package_address == package_address
298                                })
299                            {
300                                format!("Own<{}>", blueprint_name)
301                            } else {
302                                metadata_type_name.unwrap_or("Own".to_owned())
303                            }
304                        }
305                    }
306                }
307                TypeValidation::I8(_)
308                | TypeValidation::I16(_)
309                | TypeValidation::I32(_)
310                | TypeValidation::I64(_)
311                | TypeValidation::I128(_)
312                | TypeValidation::U8(_)
313                | TypeValidation::U16(_)
314                | TypeValidation::U32(_)
315                | TypeValidation::U64(_)
316                | TypeValidation::U128(_)
317                | TypeValidation::String(_)
318                | TypeValidation::Array(_)
319                | TypeValidation::Map(_)
320                | TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(..)) => {
321                    panic!("Unexpected state: an own type with non-own validation.")
322                }
323            },
324            ScryptoCustomTypeKind::Decimal => metadata_type_name.unwrap_or("Decimal".to_owned()),
325            ScryptoCustomTypeKind::PreciseDecimal => {
326                metadata_type_name.unwrap_or("PreciseDecimal".to_owned())
327            }
328            ScryptoCustomTypeKind::NonFungibleLocalId => {
329                metadata_type_name.unwrap_or("NonFungibleLocalId".to_owned())
330            }
331        },
332    };
333
334    Ok(name)
335}
336
337pub fn schema_auxiliary_types_to_ast_types<S>(
338    auxiliary_types: HashSet<ScopedTypeId>,
339    schema_resolver: &S,
340) -> Result<Vec<ast::AuxiliaryType>, schema::SchemaError>
341where
342    S: schema::PackageSchemaResolver,
343{
344    let mut ast_auxiliary_types = Vec::default();
345
346    // No auxiliary types need to be generated if they're already well-known (i.e., they can just be
347    // imported through scrypto::prelude::*). Thus, well known types are ignored.
348    for scoped_type_id in auxiliary_types
349        .into_iter()
350        .filter_map(|item| match item.1 {
351            LocalTypeId::SchemaLocalIndex(local_index) => Some((item.0, local_index)),
352            LocalTypeId::WellKnown(..) => None,
353        })
354        .collect::<BTreeSet<_>>()
355        .into_iter()
356        .map(|(schema_hash, local_type_index)| {
357            ScopedTypeId(schema_hash, LocalTypeId::SchemaLocalIndex(local_type_index))
358        })
359    {
360        let type_kind = schema_resolver.resolve_type_kind(&scoped_type_id)?;
361        let type_metadata = schema_resolver.resolve_type_metadata(&scoped_type_id)?;
362
363        let ast_auxiliary_type = match type_kind {
364            TypeKind::Tuple { field_types } => {
365                let Some(ref struct_name) = type_metadata.type_name else {
366                    /* No type name = just a tuple. No generation needed. */
367                    continue;
368                };
369
370                match type_metadata.get_field_names() {
371                    /* Named fields struct */
372                    Some(field_names) => ast::AuxiliaryType::NamedFieldsStruct {
373                        struct_name: struct_name.as_ref().into(),
374                        fields: field_names
375                            .iter()
376                            .zip(field_types)
377                            .map(|(field_name, field_type)| {
378                                let field_type_name = type_name(
379                                    &ScopedTypeId(scoped_type_id.0, field_type),
380                                    schema_resolver,
381                                )?;
382
383                                Ok((field_name.as_ref().into(), field_type_name))
384                            })
385                            .collect::<Result<_, _>>()?,
386                    },
387                    /* Tuple struct */
388                    None => ast::AuxiliaryType::TupleStruct {
389                        struct_name: struct_name.as_ref().into(),
390                        field_types: field_types
391                            .iter()
392                            .map(|field_type| {
393                                type_name(
394                                    &ScopedTypeId(scoped_type_id.0, *field_type),
395                                    schema_resolver,
396                                )
397                            })
398                            .collect::<Result<_, _>>()?,
399                    },
400                }
401            }
402            TypeKind::Enum { variants } => {
403                let enum_name = type_metadata
404                    .type_name
405                    .expect("Unexpected state: encountered an enum that has no type name!");
406
407                // TODO: Determine a better way to not generate "Option" and "Result" as auxiliary
408                // types that is not just based on the name.
409                if enum_name == "Option" || enum_name == "Result" {
410                    continue;
411                }
412
413                let Some(ChildNames::EnumVariants(variant_metadata)) = type_metadata.child_names
414                else {
415                    panic!("Unexpected state: the child names of an enum must be enum-variants")
416                };
417
418                let mut enum_variants = Vec::default();
419
420                for variant_id in variants.keys() {
421                    let field_types = variants
422                        .get(variant_id)
423                        .expect("Unexpected state: variant id can not be found!");
424                    let metadata = variant_metadata
425                        .get(variant_id)
426                        .expect("Unexpected state: variant id can not be found!");
427
428                    let variant_name = metadata
429                        .type_name
430                        .as_ref()
431                        .expect("Unexpected state: an enum variant with no name!");
432
433                    let enum_variant = if field_types.is_empty() {
434                        ast::EnumVariant::Unit {
435                            variant_name: variant_name.as_ref().into(),
436                            variant_index: *variant_id,
437                        }
438                    } else {
439                        match metadata.get_field_names() {
440                            /* Named fields struct */
441                            Some(field_names) => ast::EnumVariant::NamedFields {
442                                variant_name: variant_name.as_ref().into(),
443                                variant_index: *variant_id,
444                                fields: field_names
445                                    .iter()
446                                    .zip(field_types)
447                                    .map(|(field_name, field_type)| {
448                                        let field_type_name = type_name(
449                                            &ScopedTypeId(scoped_type_id.0, *field_type),
450                                            schema_resolver,
451                                        )?;
452
453                                        Ok((field_name.as_ref().into(), field_type_name))
454                                    })
455                                    .collect::<Result<_, _>>()?,
456                            },
457                            /* Tuple struct */
458                            None => ast::EnumVariant::Tuple {
459                                variant_name: variant_name.as_ref().into(),
460                                variant_index: *variant_id,
461                                field_types: field_types
462                                    .iter()
463                                    .map(|field_type| {
464                                        type_name(
465                                            &ScopedTypeId(scoped_type_id.0, *field_type),
466                                            schema_resolver,
467                                        )
468                                    })
469                                    .collect::<Result<_, _>>()?,
470                            },
471                        }
472                    };
473                    enum_variants.push(enum_variant)
474                }
475
476                ast::AuxiliaryType::Enum {
477                    enum_name: enum_name.into(),
478                    variants: enum_variants,
479                }
480            }
481            TypeKind::Any
482            | TypeKind::Bool
483            | TypeKind::I8
484            | TypeKind::I16
485            | TypeKind::I32
486            | TypeKind::I64
487            | TypeKind::I128
488            | TypeKind::U8
489            | TypeKind::U16
490            | TypeKind::U32
491            | TypeKind::U64
492            | TypeKind::U128
493            | TypeKind::String
494            | TypeKind::Array { .. }
495            | TypeKind::Map { .. }
496            | TypeKind::Custom(..) => {
497                /* Not considered an auxiliary type - no generation needed */
498                continue;
499            }
500        };
501
502        ast_auxiliary_types.push(ast_auxiliary_type)
503    }
504
505    ast_auxiliary_types.dedup();
506
507    Ok(ast_auxiliary_types)
508}