multiversx_sc_meta_lib/contract/sc_config/
wasm_crate_gen.rs

1use multiversx_sc::{abi::EndpointAbi, external_view_contract::EXTERNAL_VIEW_CONSTRUCTOR_FLAG};
2use std::{
3    fs::{self, File},
4    io::Write,
5    path::{Path, PathBuf},
6};
7
8use super::ContractVariant;
9
10const PREFIX_AUTO_GENERATED: &str =
11    "// Code generated by the multiversx-sc build system. DO NOT EDIT.
12
13////////////////////////////////////////////////////
14////////////////// AUTO-GENERATED //////////////////
15////////////////////////////////////////////////////
16";
17
18const NO_STD_ANNOTATION: &str = "#![no_std]
19";
20
21const NUM_INIT: usize = 1;
22const NUM_UPGRADE: usize = 1;
23const NUM_ASYNC_CB: usize = 1;
24
25impl ContractVariant {
26    /// Makes sure that all the necessary wasm crate directories exist.
27    pub fn create_wasm_crate_dir(&self) {
28        fs::create_dir_all(PathBuf::from(&self.wasm_crate_path()).join("src")).unwrap();
29    }
30
31    fn allocator_macro_invocation(&self) -> String {
32        format!(
33            "multiversx_sc_wasm_adapter::allocator!({});",
34            self.settings.allocator.to_allocator_macro_selector()
35        )
36    }
37
38    #[allow(clippy::collapsible_else_if)]
39    fn panic_handler_macro_invocation(&self) -> &'static str {
40        if self.settings.std {
41            if self.settings.panic_message {
42                "multiversx_sc_wasm_adapter::panic_handler_std_with_message!();"
43            } else {
44                "multiversx_sc_wasm_adapter::panic_handler_std!();"
45            }
46        } else {
47            if self.settings.panic_message {
48                "multiversx_sc_wasm_adapter::panic_handler_with_message!();"
49            } else {
50                "multiversx_sc_wasm_adapter::panic_handler!();"
51            }
52        }
53    }
54
55    fn endpoint_macro_name(&self) -> &'static str {
56        if self.settings.external_view {
57            "multiversx_sc_wasm_adapter::external_view_endpoints!"
58        } else {
59            "multiversx_sc_wasm_adapter::endpoints!"
60        }
61    }
62
63    /// Generates the wasm crate lib.rs source, st the given path.
64    pub fn generate_wasm_src_lib_file(&self) {
65        let lib_path = Path::new(&self.wasm_crate_path())
66            .join("src")
67            .join("lib.rs");
68        let mut wasm_lib_file = File::create(lib_path).unwrap();
69        self.write_wasm_src_lib_contents(&mut wasm_lib_file);
70    }
71
72    fn write_wasm_src_lib_contents(&self, wasm_lib_file: &mut File) {
73        writeln!(wasm_lib_file, "{PREFIX_AUTO_GENERATED}").unwrap();
74        self.write_stat_comments(wasm_lib_file);
75        self.write_no_std_annotation(wasm_lib_file);
76        writeln!(wasm_lib_file, "{}", self.allocator_macro_invocation()).unwrap();
77        writeln!(wasm_lib_file, "{}", self.panic_handler_macro_invocation()).unwrap();
78
79        if self.settings.external_view {
80            write_external_view_init(wasm_lib_file);
81        }
82
83        let contract_module_name = self.abi.get_crate_name_for_code();
84        write_endpoints_macro(
85            self.endpoint_macro_name(),
86            wasm_lib_file,
87            &contract_module_name,
88            self.abi.iter_all_exports(),
89        );
90
91        write_async_callback_macro(wasm_lib_file, self.abi.has_callback, &contract_module_name);
92    }
93}
94
95fn write_stat_comment(wasm_lib_file: &mut File, label: &str, number: usize) {
96    writeln!(wasm_lib_file, "// {label:<35} {number:3}").unwrap();
97}
98
99impl ContractVariant {
100    /// Writing some nicely formatted comments breaking down all exported functions.
101    fn write_stat_comments(&self, wasm_lib_file: &mut File) {
102        let mut total = self.abi.endpoints.len() + NUM_ASYNC_CB + self.abi.promise_callbacks.len();
103
104        if !self.abi.constructors.is_empty() {
105            write_stat_comment(wasm_lib_file, "Init:", NUM_INIT);
106            total += NUM_INIT;
107        }
108        if !self.abi.upgrade_constructors.is_empty() {
109            write_stat_comment(wasm_lib_file, "Upgrade:", NUM_UPGRADE);
110            total += NUM_UPGRADE;
111        }
112
113        write_stat_comment(wasm_lib_file, "Endpoints:", self.abi.endpoints.len());
114        if self.abi.has_callback {
115            write_stat_comment(wasm_lib_file, "Async Callback:", NUM_ASYNC_CB);
116        } else {
117            write_stat_comment(wasm_lib_file, "Async Callback (empty):", NUM_ASYNC_CB);
118        }
119        if !self.abi.promise_callbacks.is_empty() {
120            write_stat_comment(
121                wasm_lib_file,
122                "Promise callbacks:",
123                self.abi.promise_callbacks.len(),
124            );
125        }
126
127        write_stat_comment(wasm_lib_file, "Total number of exported functions:", total);
128
129        writeln!(wasm_lib_file).unwrap();
130    }
131
132    fn write_no_std_annotation(&self, wasm_lib_file: &mut File) {
133        if !self.settings.std {
134            write_prefix(wasm_lib_file, NO_STD_ANNOTATION);
135        }
136    }
137}
138
139fn write_prefix(wasm_lib_file: &mut File, features: &str) {
140    writeln!(wasm_lib_file, "{}", features).unwrap();
141}
142
143fn write_endpoints_macro<'a, I>(
144    full_macro_name: &str,
145    wasm_lib_file: &mut File,
146    contract_module_name: &str,
147    endpoint_iter: I,
148) where
149    I: Iterator<Item = &'a EndpointAbi>,
150{
151    writeln!(wasm_lib_file).unwrap();
152    writeln!(wasm_lib_file, "{full_macro_name} {{").unwrap();
153    writeln!(wasm_lib_file, "    {contract_module_name}").unwrap();
154    writeln!(wasm_lib_file, "    (").unwrap();
155    for endpoint in endpoint_iter {
156        if endpoint.rust_method_name == EXTERNAL_VIEW_CONSTRUCTOR_FLAG {
157            continue;
158        }
159        writeln!(
160            wasm_lib_file,
161            "        {} => {}",
162            endpoint.name, endpoint.rust_method_name
163        )
164        .unwrap();
165    }
166    writeln!(wasm_lib_file, "    )").unwrap();
167    writeln!(wasm_lib_file, "}}").unwrap();
168}
169
170fn write_async_callback_macro(
171    wasm_lib_file: &mut File,
172    has_callback: bool,
173    contract_module_name: &str,
174) {
175    writeln!(wasm_lib_file).unwrap();
176    if has_callback {
177        writeln!(
178            wasm_lib_file,
179            "multiversx_sc_wasm_adapter::async_callback! {{ {contract_module_name} }}"
180        )
181        .unwrap();
182    } else {
183        writeln!(
184            wasm_lib_file,
185            "multiversx_sc_wasm_adapter::async_callback_empty! {{}}",
186        )
187        .unwrap();
188    }
189}
190
191fn write_external_view_init(wasm_lib_file: &mut File) {
192    writeln!(wasm_lib_file).unwrap();
193    writeln!(
194        wasm_lib_file,
195        "multiversx_sc_wasm_adapter::external_view_init! {{}}",
196    )
197    .unwrap();
198}