1#[macro_export]
17macro_rules! checksum {
18 ($bytes: expr) => {{
19 use sha2::Digest;
20 hex::encode(&sha2::Sha256::digest($bytes))
21 }};
22}
23
24#[macro_export]
25macro_rules! checksum_error {
26 ($expected: expr, $candidate: expr) => {
27 Err($crate::errors::ParameterError::ChecksumMismatch($expected, $candidate))
28 };
29}
30
31#[macro_export]
32macro_rules! remove_file {
33 ($filepath:expr) => {
34 #[cfg(not(feature = "wasm"))]
36 if std::path::PathBuf::from(&$filepath).exists() {
37 match std::fs::remove_file(&$filepath) {
38 Ok(()) => println!("Removed {:?}. Please retry the command.", $filepath),
39 Err(err) => eprintln!("Failed to remove {:?}: {err}", $filepath),
40 }
41 }
42 };
43}
44
45macro_rules! impl_store_and_remote_fetch {
46 () => {
47 #[cfg(not(feature = "wasm"))]
48 fn store_bytes(buffer: &[u8], file_path: &std::path::Path) -> Result<(), $crate::errors::ParameterError> {
49 use snarkvm_utilities::Write;
50
51 #[cfg(not(feature = "no_std_out"))]
52 {
53 use colored::*;
54 let output = format!("{:>15} - Storing file in {:?}", "Installation", file_path);
55 println!("{}", output.dimmed());
56 }
57
58 let mut directory_path = file_path.to_path_buf();
60 directory_path.pop();
61 let _ = std::fs::create_dir_all(directory_path)?;
62
63 match std::fs::File::create(file_path) {
65 Ok(mut file) => file.write_all(&buffer)?,
66 Err(error) => eprintln!("{}", error),
67 }
68 Ok(())
69 }
70
71 #[cfg(not(feature = "wasm"))]
72 fn remote_fetch(buffer: &mut Vec<u8>, url: &str) -> Result<(), $crate::errors::ParameterError> {
73 let mut easy = curl::easy::Easy::new();
74 easy.follow_location(true)?;
75 easy.url(url)?;
76
77 #[cfg(not(feature = "no_std_out"))]
78 {
79 use colored::*;
80
81 let output = format!("{:>15} - Downloading \"{}\"", "Installation", url);
82 println!("{}", output.dimmed());
83
84 easy.progress(true)?;
85 easy.progress_function(|total_download, current_download, _, _| {
86 let percent = (current_download / total_download) * 100.0;
87 let size_in_megabytes = total_download as u64 / 1_048_576;
88 let output = format!(
89 "\r{:>15} - {:.2}% complete ({:#} MB total)",
90 "Installation", percent, size_in_megabytes
91 );
92 print!("{}", output.dimmed());
93 true
94 })?;
95 }
96
97 let mut transfer = easy.transfer();
98 transfer.write_function(|data| {
99 buffer.extend_from_slice(data);
100 Ok(data.len())
101 })?;
102 Ok(transfer.perform()?)
103 }
104
105 #[cfg(feature = "wasm")]
106 fn remote_fetch(url: &str) -> Result<Vec<u8>, $crate::errors::ParameterError> {
107 let xhr = web_sys::XmlHttpRequest::new().map_err(|_| {
112 $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest object not found".to_string())
113 })?;
114
115 xhr.override_mime_type("octet/binary; charset=ISO-8859-5").unwrap();
119
120 xhr.open_with_async("GET", url, false).map_err(|_| {
122 $crate::errors::ParameterError::Wasm(
123 "Download failed - This browser does not support synchronous requests".to_string(),
124 )
125 })?;
126 xhr.send().map_err(|_| {
127 $crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string())
128 })?;
129
130 if xhr.response().is_ok() && xhr.status().unwrap() == 200 {
132 let rust_text = xhr
134 .response_text()
135 .map_err(|_| $crate::errors::ParameterError::Wasm("XMLHttpRequest failed".to_string()))?
136 .ok_or($crate::errors::ParameterError::Wasm(
137 "The request was successful but no parameters were received".to_string(),
138 ))?;
139
140 use encoding::Encoding;
142 encoding::all::ISO_8859_5
143 .encode(&rust_text, encoding::EncoderTrap::Strict)
144 .map_err(|_| $crate::errors::ParameterError::Wasm("Parameter decoding failed".to_string()))
145 } else {
146 Err($crate::errors::ParameterError::Wasm("Download failed - XMLHttpRequest failed".to_string()))
147 }
148 }
149 };
150}
151
152macro_rules! impl_load_bytes_logic_local {
153 ($filepath: expr, $buffer: expr, $expected_size: expr, $expected_checksum: expr) => {
154 if $expected_size != $buffer.len() {
156 remove_file!($filepath);
157 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, $buffer.len()));
158 }
159
160 let candidate_checksum = checksum!($buffer);
162 if $expected_checksum != candidate_checksum {
163 return checksum_error!($expected_checksum, candidate_checksum);
164 }
165
166 return Ok($buffer.to_vec());
167 };
168}
169
170macro_rules! impl_load_bytes_logic_remote {
171 ($remote_url: expr, $local_dir: expr, $filename: expr, $metadata: expr, $expected_checksum: expr, $expected_size: expr) => {
172 let mut file_path = aleo_std::aleo_dir();
174 file_path.push($local_dir);
175 file_path.push($filename);
176
177 let buffer = if file_path.exists() {
178 std::fs::read(&file_path)?
180 } else {
181 #[cfg(not(feature = "no_std_out"))]
183 {
184 use colored::*;
185 let path = format!("(in {:?})", file_path);
186 eprintln!(
187 "\n⚠️ \"{}\" does not exist. Downloading and storing it {}.\n",
188 $filename, path.dimmed()
189 );
190 }
191
192 let url = format!("{}/{}", $remote_url, $filename);
194
195 cfg_if::cfg_if! {
197 if #[cfg(not(feature = "wasm"))] {
198 let mut buffer = vec![];
199 Self::remote_fetch(&mut buffer, &url)?;
200
201 let candidate_checksum = checksum!(&buffer);
203 if $expected_checksum != candidate_checksum {
204 return checksum_error!($expected_checksum, candidate_checksum)
205 }
206
207 match Self::store_bytes(&buffer, &file_path) {
208 Ok(()) => buffer,
209 Err(_) => {
210 eprintln!(
211 "\n❗ Error - Failed to store \"{}\" locally. Please download this file manually and ensure it is stored in {:?}.\n",
212 $filename, file_path
213 );
214 buffer
215 }
216 }
217 } else if #[cfg(feature = "wasm")] {
218 let buffer = Self::remote_fetch(&url)?;
219
220 let candidate_checksum = checksum!(&buffer);
222 if $expected_checksum != candidate_checksum {
223 return checksum_error!($expected_checksum, candidate_checksum)
224 }
225
226 buffer
227 } else {
228 return Err($crate::errors::ParameterError::RemoteFetchDisabled);
229 }
230 }
231 };
232
233 if $expected_size != buffer.len() {
235 remove_file!(file_path);
236 return Err($crate::errors::ParameterError::SizeMismatch($expected_size, buffer.len()));
237 }
238
239 let candidate_checksum = checksum!(buffer.as_slice());
241 if $expected_checksum != candidate_checksum {
242 return checksum_error!($expected_checksum, candidate_checksum)
243 }
244
245 return Ok(buffer)
246 }
247}
248
249#[macro_export]
250macro_rules! impl_local {
251 ($name: ident, $local_dir: expr, $fname: tt, "usrs") => {
252 #[derive(Clone, Debug, PartialEq, Eq)]
253 pub struct $name;
254
255 impl $name {
256 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
257
258 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
259 let metadata: serde_json::Value =
260 serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
261 let expected_checksum: String =
262 metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
263 let expected_size: usize =
264 metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
265
266 let _filepath = concat!($local_dir, $fname, ".", "usrs");
267 let buffer = include_bytes!(concat!($local_dir, $fname, ".", "usrs"));
268
269 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
270 }
271 }
272
273 paste::item! {
274 #[cfg(test)]
275 #[test]
276 fn [< test_ $fname _usrs >]() {
277 assert!($name::load_bytes().is_ok());
278 }
279 }
280 };
281 ($name: ident, $local_dir: expr, $fname: tt, $ftype: tt) => {
282 #[derive(Clone, Debug, PartialEq, Eq)]
283 pub struct $name;
284
285 impl $name {
286 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
287
288 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
289 let metadata: serde_json::Value =
290 serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
291 let expected_checksum: String =
292 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
293 let expected_size: usize =
294 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
295
296 let _filepath = concat!($local_dir, $fname, ".", $ftype);
297 let buffer = include_bytes!(concat!($local_dir, $fname, ".", $ftype));
298
299 impl_load_bytes_logic_local!(_filepath, buffer, expected_size, expected_checksum);
300 }
301 }
302
303 paste::item! {
304 #[cfg(test)]
305 #[test]
306 fn [< test_ $fname _ $ftype >]() {
307 assert!($name::load_bytes().is_ok());
308 }
309 }
310 };
311}
312
313#[macro_export]
314macro_rules! impl_remote {
315 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, "usrs") => {
316 pub struct $name;
317
318 impl $name {
319 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
320
321 impl_store_and_remote_fetch!();
322
323 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
324 let metadata: serde_json::Value =
325 serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
326 let expected_checksum: String =
327 metadata["checksum"].as_str().expect("Failed to parse checksum").to_string();
328 let expected_size: usize =
329 metadata["size"].to_string().parse().expect("Failed to retrieve the file size");
330
331 let filename = match expected_checksum.get(0..7) {
333 Some(sum) => format!("{}.{}.{}", $fname, "usrs", sum),
334 _ => format!("{}.{}", $fname, "usrs"),
335 };
336
337 impl_load_bytes_logic_remote!(
338 $remote_url,
339 $local_dir,
340 &filename,
341 metadata,
342 expected_checksum,
343 expected_size
344 );
345 }
346 }
347 paste::item! {
348 #[cfg(test)]
349 #[test]
350 fn [< test_ $fname _usrs >]() {
351 assert!($name::load_bytes().is_ok());
352 }
353 }
354 };
355 ($name: ident, $remote_url: expr, $local_dir: expr, $fname: tt, $ftype: tt) => {
356 pub struct $name;
357
358 impl $name {
359 pub const METADATA: &'static str = include_str!(concat!($local_dir, $fname, ".metadata"));
360
361 impl_store_and_remote_fetch!();
362
363 pub fn load_bytes() -> Result<Vec<u8>, $crate::errors::ParameterError> {
364 let metadata: serde_json::Value =
365 serde_json::from_str(Self::METADATA).expect("Metadata was not well-formatted");
366 let expected_checksum: String =
367 metadata[concat!($ftype, "_checksum")].as_str().expect("Failed to parse checksum").to_string();
368 let expected_size: usize =
369 metadata[concat!($ftype, "_size")].to_string().parse().expect("Failed to retrieve the file size");
370
371 let filename = match expected_checksum.get(0..7) {
373 Some(sum) => format!("{}.{}.{}", $fname, $ftype, sum),
374 _ => format!("{}.{}", $fname, $ftype),
375 };
376
377 impl_load_bytes_logic_remote!(
378 $remote_url,
379 $local_dir,
380 &filename,
381 metadata,
382 expected_checksum,
383 expected_size
384 );
385 }
386 }
387
388 paste::item! {
389 #[cfg(test)]
390 #[test]
391 fn [< test_ $fname _ $ftype >]() {
392 assert!($name::load_bytes().is_ok());
393 }
394 }
395 };
396}