multiversx_sc_meta_lib/contract/sc_config/
wasm_crate_gen.rsuse multiversx_sc::{abi::EndpointAbi, external_view_contract::EXTERNAL_VIEW_CONSTRUCTOR_FLAG};
use rustc_version::Version;
use std::{
fs::{self, File},
io::Write,
path::PathBuf,
str::FromStr,
};
use super::ContractVariant;
const PREFIX_AUTO_GENERATED: &str =
"// Code generated by the multiversx-sc build system. DO NOT EDIT.
////////////////////////////////////////////////////
////////////////// AUTO-GENERATED //////////////////
////////////////////////////////////////////////////
";
const NUM_INIT: usize = 1;
const NUM_UPGRADE: usize = 1;
const NUM_ASYNC_CB: usize = 1;
const VER_1_71: &str = "1.71.0-nightly";
const FEATURES_PRE_RUSTC_1_71: &str = "
#![no_std]
// Configuration that works with rustc < 1.71.0.
// TODO: Recommended rustc version: 1.73.0 or newer.
#![feature(alloc_error_handler)]
";
const VER_1_73: &str = "1.73.0-nightly";
const FEATURES_PRE_RUSTC_1_73: &str = "
#![no_std]
// Configuration that works with rustc < 1.73.0.
// TODO: Recommended rustc version: 1.73.0 or newer.
";
const FEATURES_DEFAULT: &str = "
#![no_std]
";
impl ContractVariant {
pub fn create_wasm_crate_dir(&self) {
fs::create_dir_all(PathBuf::from(&self.wasm_crate_path()).join("src")).unwrap();
}
fn allocator_macro_invocation(&self) -> String {
format!(
"multiversx_sc_wasm_adapter::allocator!({});",
self.settings.allocator.to_allocator_macro_selector()
)
}
fn panic_handler_macro_invocation(&self) -> &'static str {
if self.settings.panic_message {
"multiversx_sc_wasm_adapter::panic_handler_with_message!();"
} else {
"multiversx_sc_wasm_adapter::panic_handler!();"
}
}
fn endpoint_macro_name(&self) -> &'static str {
if self.settings.external_view {
"multiversx_sc_wasm_adapter::external_view_endpoints!"
} else {
"multiversx_sc_wasm_adapter::endpoints!"
}
}
pub fn generate_wasm_src_lib_file(&self) {
let lib_path = format!("{}/src/lib.rs", &self.wasm_crate_path());
let mut wasm_lib_file = File::create(lib_path).unwrap();
self.write_wasm_src_lib_contents(&mut wasm_lib_file);
}
fn write_wasm_src_lib_contents(&self, wasm_lib_file: &mut File) {
writeln!(wasm_lib_file, "{PREFIX_AUTO_GENERATED}").unwrap();
self.write_stat_comments(wasm_lib_file);
write_prefix(wasm_lib_file);
writeln!(wasm_lib_file, "{}", self.allocator_macro_invocation()).unwrap();
writeln!(wasm_lib_file, "{}", self.panic_handler_macro_invocation()).unwrap();
if self.settings.external_view {
write_external_view_init(wasm_lib_file);
}
let contract_module_name = self.abi.get_crate_name_for_code();
write_endpoints_macro(
self.endpoint_macro_name(),
wasm_lib_file,
&contract_module_name,
self.abi.iter_all_exports(),
);
write_async_callback_macro(wasm_lib_file, self.abi.has_callback, &contract_module_name);
}
}
fn write_stat_comment(wasm_lib_file: &mut File, label: &str, number: usize) {
writeln!(wasm_lib_file, "// {label:<35} {number:3}").unwrap();
}
impl ContractVariant {
fn write_stat_comments(&self, wasm_lib_file: &mut File) {
let mut total = self.abi.endpoints.len() + NUM_ASYNC_CB + self.abi.promise_callbacks.len();
if !self.abi.constructors.is_empty() {
write_stat_comment(wasm_lib_file, "Init:", NUM_INIT);
total += NUM_INIT;
}
if !self.abi.upgrade_constructors.is_empty() {
write_stat_comment(wasm_lib_file, "Upgrade:", NUM_UPGRADE);
total += NUM_UPGRADE;
}
write_stat_comment(wasm_lib_file, "Endpoints:", self.abi.endpoints.len());
if self.abi.has_callback {
write_stat_comment(wasm_lib_file, "Async Callback:", NUM_ASYNC_CB);
} else {
write_stat_comment(wasm_lib_file, "Async Callback (empty):", NUM_ASYNC_CB);
}
if !self.abi.promise_callbacks.is_empty() {
write_stat_comment(
wasm_lib_file,
"Promise callbacks:",
self.abi.promise_callbacks.len(),
);
}
write_stat_comment(wasm_lib_file, "Total number of exported functions:", total);
}
}
fn select_features() -> &'static str {
let meta = rustc_version::version_meta().unwrap();
if meta.semver < Version::from_str(VER_1_71).unwrap() {
FEATURES_PRE_RUSTC_1_71
} else if meta.semver < Version::from_str(VER_1_73).unwrap() {
FEATURES_PRE_RUSTC_1_73
} else {
FEATURES_DEFAULT
}
}
fn write_prefix(wasm_lib_file: &mut File) {
writeln!(wasm_lib_file, "{}", select_features()).unwrap();
}
fn write_endpoints_macro<'a, I>(
full_macro_name: &str,
wasm_lib_file: &mut File,
contract_module_name: &str,
endpoint_iter: I,
) where
I: Iterator<Item = &'a EndpointAbi>,
{
writeln!(wasm_lib_file).unwrap();
writeln!(wasm_lib_file, "{full_macro_name} {{").unwrap();
writeln!(wasm_lib_file, " {contract_module_name}").unwrap();
writeln!(wasm_lib_file, " (").unwrap();
for endpoint in endpoint_iter {
if endpoint.rust_method_name == EXTERNAL_VIEW_CONSTRUCTOR_FLAG {
continue;
}
writeln!(
wasm_lib_file,
" {} => {}",
endpoint.name, endpoint.rust_method_name
)
.unwrap();
}
writeln!(wasm_lib_file, " )").unwrap();
writeln!(wasm_lib_file, "}}").unwrap();
}
fn write_async_callback_macro(
wasm_lib_file: &mut File,
has_callback: bool,
contract_module_name: &str,
) {
writeln!(wasm_lib_file).unwrap();
if has_callback {
writeln!(
wasm_lib_file,
"multiversx_sc_wasm_adapter::async_callback! {{ {contract_module_name} }}"
)
.unwrap();
} else {
writeln!(
wasm_lib_file,
"multiversx_sc_wasm_adapter::async_callback_empty! {{}}",
)
.unwrap();
}
}
fn write_external_view_init(wasm_lib_file: &mut File) {
writeln!(wasm_lib_file).unwrap();
writeln!(
wasm_lib_file,
"multiversx_sc_wasm_adapter::external_view_init! {{}}",
)
.unwrap();
}