1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
use js_sys::{Array, Function, JsString, Object};
use std::sync::atomic::{AtomicBool, Ordering};
use wasm_bindgen::prelude::*;
#[doc(hidden)]
pub mod __macrodeps {
pub use gensym::gensym;
pub use paste::item;
pub use wasm_bindgen::prelude::wasm_bindgen;
}
static INIT_DONE: AtomicBool = AtomicBool::new(false);
#[cfg(feature = "auto-init")]
#[wasm_bindgen(start)]
fn main() {
wasm_init();
}
/// This function will call all instances of [`crate::wasm_init!`].
///
/// This function can be called either:
/// - from Rust as part of an entrypoint
/// - from JavaScript/TypeScript by calling the exported `wasm_init` function in your module
/// - automatically, by enabling the "auto-init" feature
///
/// This function is idempotent, or safe to call multiple times;
/// your [`crate::wasm_init!`] calls will only be executed once.
#[wasm_bindgen]
pub fn wasm_init() {
if INIT_DONE.swap(true, Ordering::Relaxed) {
return;
};
let exports: Object = wasm_bindgen::exports().into();
let entries = Object::entries(&exports);
for entry in entries {
let entry = Array::from(&entry);
let name: JsString = entry.get(0).into();
let func: Function = entry.get(1).into();
if name.starts_with("__wasm_init", 0) {
func.apply(&JsValue::undefined(), &Array::new())
.expect("func invocation failed");
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __wasm_init_impl {
($gensym:ident, $($input:tt)*) => {
$crate::__macrodeps::item! {
#[$crate::__macrodeps::wasm_bindgen]
pub fn [<__wasm_init $gensym>]() {
$($input)*
}
}
};
}
/// Register code to be run on start-up.
///
/// Each call to this macro will generate a function that will be called exactly once,
/// after the [wasm_init] function is called for the first time.
///
/// You can register code from as many different crates in your project as you'd like;
/// [wasm_init] only needs to be called once.
///
/// # Examples
/// ```
/// trait Plugin {}
///
/// fn register_plugin(plugin: impl Plugin) {
/// // grab data from each plugin and store them in a global somewhere
/// }
///
/// struct Plugin1;
///
/// wasm_init::wasm_init! {
/// register_plugin(Plugin1);
/// }
///
/// struct Plugin2;
///
/// wasm_init::wasm_init! {
/// register_plugin(Plugin2);
/// }
/// ```
#[macro_export]
macro_rules! wasm_init {
($($input:tt)*) => {
$crate::__macrodeps::gensym! { $crate::__wasm_init_impl! { $($input)* } }
};
}