multiversx_sc_meta_lib/contract/generate_snippets/
snippet_crate_gen.rs

1use colored::Colorize;
2use std::{
3    fs::{self, File, OpenOptions},
4    io::Write,
5    path::{Path, PathBuf},
6};
7
8use crate::version_history;
9
10static SNIPPETS_SOURCE_FILE_NAME: &str = "interactor_main.rs";
11static LIB_SOURCE_FILE_NAME: &str = "interact.rs";
12static SC_CONFIG_FILE_NAME: &str = "sc-config.toml";
13static CONFIG_TOML_PATH: &str = "config.toml";
14static CONFIG_SOURCE_FILE_NAME: &str = "config.rs";
15static PROXY_FILE_NAME: &str = "_proxy.rs";
16static INTERACTOR_CS_TEST_FILE_NAME: &str = "interact_cs_tests.rs";
17static INTERACTOR_TEST_FILE_NAME: &str = "interact_tests.rs";
18
19pub(crate) fn create_snippets_folder(snippets_folder_path: &PathBuf) {
20    let _ = fs::create_dir(snippets_folder_path);
21}
22
23pub(crate) fn create_snippets_gitignore(snippets_folder_path: &Path, overwrite: bool) {
24    let gitignore_path = snippets_folder_path.join(".gitignore");
25    let mut file = if overwrite {
26        File::create(&gitignore_path).unwrap()
27    } else {
28        match File::options()
29            .create_new(true)
30            .write(true)
31            .open(&gitignore_path)
32        {
33            Ok(f) => f,
34            Err(_) => return,
35        }
36    };
37
38    writeln!(
39        &mut file,
40        "# Pem files are used for interactions, but shouldn't be committed
41*.pem"
42    )
43    .unwrap();
44}
45
46pub(crate) fn create_snippets_cargo_toml(
47    snippets_folder_path: &Path,
48    contract_crate_name: &str,
49    overwrite: bool,
50) {
51    let contract_deps = contract_crate_name.replace("_", "-");
52    let cargo_toml_path = snippets_folder_path.join("Cargo.toml");
53    let mut file = if overwrite {
54        File::create(&cargo_toml_path).unwrap()
55    } else {
56        match File::options()
57            .create_new(true)
58            .write(true)
59            .open(&cargo_toml_path)
60        {
61            Ok(f) => f,
62            Err(_) => return,
63        }
64    };
65
66    let last_release_version = &version_history::LAST_VERSION;
67
68    writeln!(
69        &mut file,
70        r#"[package]
71name = "rust-interact"
72version = "0.0.0"
73authors = ["you"]
74edition = "2021"
75publish = false
76
77[[bin]]
78name = "rust-interact"
79path = "src/{SNIPPETS_SOURCE_FILE_NAME}"
80
81[lib]
82path = "src/{LIB_SOURCE_FILE_NAME}"
83
84[dependencies.{contract_deps}]
85path = ".."
86
87[dependencies.multiversx-sc-snippets]
88version = "{last_release_version}"
89
90[dependencies.multiversx-sc]
91version = "{last_release_version}"
92
93[dependencies]
94clap = {{ version = "4.4.7", features = ["derive"] }}
95serde = {{ version = "1.0", features = ["derive"] }}
96toml = "0.9"
97
98[features]
99chain-simulator-tests = []
100"#
101    )
102    .unwrap();
103}
104
105pub(crate) fn create_src_folder(snippets_folder_path: &Path) {
106    // returns error if folder already exists, so we ignore the result
107    let src_folder_path = snippets_folder_path.join("src");
108    let _ = fs::create_dir(src_folder_path);
109}
110
111#[must_use]
112pub(crate) fn create_and_get_lib_file(snippets_folder_path: &Path, overwrite: bool) -> File {
113    let lib_path = snippets_folder_path.join("src").join(LIB_SOURCE_FILE_NAME);
114    if overwrite {
115        File::create(&lib_path).unwrap()
116    } else {
117        match File::options().create_new(true).write(true).open(&lib_path) {
118            Ok(f) => f,
119            Err(_) => {
120                println!(
121                    "{}",
122                    format!(
123                        "{lib_path:#?} file already exists, --overwrite option was not provided",
124                    )
125                    .yellow()
126                );
127                File::options().write(true).open(&lib_path).unwrap()
128            }
129        }
130    }
131}
132
133pub(crate) fn create_main_file(snippets_folder_path: &Path, contract_crate_name: &str) {
134    let lib_path = snippets_folder_path
135        .join("src")
136        .join(SNIPPETS_SOURCE_FILE_NAME);
137
138    let mut file = File::create(lib_path).unwrap();
139
140    writeln!(
141        &mut file,
142        r#"
143use multiversx_sc_snippets::imports::*;
144use rust_interact::{contract_crate_name}_cli;
145
146#[tokio::main]
147async fn main() {{
148    {contract_crate_name}_cli().await;
149}}  
150"#
151    )
152    .unwrap();
153}
154
155pub(crate) fn create_test_folder_and_get_files(snippets_folder_path: &Path) -> (File, File) {
156    let folder_path = snippets_folder_path.join("tests");
157
158    if !Path::new(&folder_path).exists() {
159        fs::create_dir_all(&folder_path).expect("Failed to create tests directory");
160    }
161
162    let interactor_file_path = folder_path.join(INTERACTOR_TEST_FILE_NAME);
163    let interactor_cs_file_path = folder_path.join(INTERACTOR_CS_TEST_FILE_NAME);
164
165    let interactor_file =
166        File::create(interactor_file_path).expect("Failed to create interact_tests.rs file");
167    let interactor_cs_file =
168        File::create(interactor_cs_file_path).expect("Failed to create interact_cs_tests.rs file");
169
170    (interactor_file, interactor_cs_file)
171}
172
173pub(crate) fn create_sc_config_file(overwrite: bool, contract_crate_name: &str) {
174    let sc_config_path = Path::new("..").join(SC_CONFIG_FILE_NAME);
175    let proxy_name = contract_crate_name.replace("-", "_") + PROXY_FILE_NAME;
176
177    // check if the file should be overwritten or if it already exists
178    let mut file = if overwrite || !sc_config_path.exists() {
179        File::create(sc_config_path).unwrap()
180    } else {
181        // file already exists
182        let file = OpenOptions::new()
183            .read(true)
184            .append(true)
185            .open(&sc_config_path)
186            .unwrap();
187
188        if file_contains_proxy_path(&sc_config_path, &proxy_name).unwrap_or(false) {
189            return;
190        }
191
192        file
193    };
194
195    // will be deserialized into a PathBuf, which normalizes the path depending on the platform
196    // when deserializing from toml, backwards slashes are not allowed
197    let full_proxy_entry = r#"
198[[proxy]]
199path = "interactor/src"#;
200
201    // write full proxy toml entry to the file
202    writeln!(&mut file, "\n{full_proxy_entry}/{proxy_name}\"").unwrap();
203}
204
205pub(crate) fn create_config_toml_file(snippets_folder_path: &Path) {
206    let config_path = snippets_folder_path.join(CONFIG_TOML_PATH);
207    let mut file = File::create(config_path).unwrap();
208
209    writeln!(
210        &mut file,
211        r#"
212# chain_type = 'simulator'
213# gateway_uri = 'http://localhost:8085'
214
215chain_type = 'real'
216gateway_uri = 'https://devnet-gateway.multiversx.com'
217"#
218    )
219    .unwrap();
220}
221
222pub(crate) fn create_config_rust_file(snippets_folder_path: &Path) -> File {
223    let lib_path = snippets_folder_path
224        .join("src")
225        .join(CONFIG_SOURCE_FILE_NAME);
226
227    File::create(lib_path).unwrap()
228}
229
230fn file_contains_proxy_path(file_path: &PathBuf, proxy_name: &str) -> std::io::Result<bool> {
231    let file_content = fs::read_to_string(file_path)?;
232
233    let proxy_path = Path::new("interactor").join("src").join(proxy_name);
234    let proxy_entry = format!("path = \"{}\"", &proxy_path.to_string_lossy());
235
236    Ok(file_content.contains(&proxy_entry))
237}