//! wasm entry points called from `template.js`, plus the host imports they need
//! for saving and reloading config files.
use core::sync::atomic::{AtomicU64, Ordering};
unsafe extern "C" {
/// Trigger a browser download of `name` containing `data`.
fn download_text(name_ptr: *const u8, name_len: usize, data_ptr: *const u8, data_len: usize);
/// Pull the most recently loaded config file's text into wasm memory.
fn config_text(out_ptr: *mut u8, out_cap: usize) -> usize;
}
/// Process-global seed counter; wasm has no clock, so we advance this each call
/// to vary generated data between clicks.
static SEED: AtomicU64 = AtomicU64::new(0xDEAD_BEEF_CAFE_F00D);
fn next_seed() -> u64 {
SEED.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed)
}
#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn run() {
use crate::wasm::{utils, view};
utils::log("Rust WASM: Auto-start function executed.");
view::render();
}
/// Re-scan the page and re-render the UI. Exposed so the rescan button in the
/// view can trigger a refresh.
#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn rescan() {
crate::wasm::view::render();
}
/// Sample grid `index`, generate `count` similar synthetic rows (or as many as
/// were observed when `count == 0`), and inject them back into the grid.
#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn generate(index: usize, count: usize) {
use crate::wasm::{grid, rng::Rng, utils, view};
let table = grid::read_table(index);
let n = if count == 0 {
table.row_count.max(1)
} else {
count
};
let mut rng = Rng::new(next_seed());
let payload = table.generate(n, &mut rng);
let ok = grid::inject(index, &payload);
utils::log(format!(
"hecatomb: generated {} {} rows for grid {} ({})",
n,
table.row_model.label(),
index,
if ok { "injected" } else { "no grid api" }
));
view::render();
}
/// Sample grid `index` and download its schema as a per-table config file,
/// recording `count` as the default generation size.
#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn save_config(index: usize, count: usize) {
use crate::wasm::{config, grid};
let mut table = grid::read_table(index);
table.gen_count = if count == 0 {
table.row_count.max(1)
} else {
count
};
let text = config::to_text(&table);
let stem = if table.id.is_empty() {
format!("grid-{}", index)
} else {
table.id.clone()
};
let name = format!("{}.heca.txt", stem);
unsafe {
download_text(name.as_ptr(), name.len(), text.as_ptr(), text.len());
}
}
/// Apply a previously loaded config file (pulled from the JS side) to grid
/// `index`: parse it, generate the configured number of rows, and inject.
#[cfg(target_arch = "wasm32")]
#[unsafe(no_mangle)]
pub extern "C" fn apply_config(index: usize) {
use crate::wasm::{config, grid, rng::Rng, utils, view, wire};
let bytes = wire::pull(|ptr, cap| unsafe { config_text(ptr, cap) });
let text = String::from_utf8_lossy(&bytes).into_owned();
if text.trim().is_empty() {
return;
}
let table = config::from_text(&text);
let mut rng = Rng::new(next_seed());
let payload = table.generate(table.gen_count.max(1), &mut rng);
let ok = grid::inject(index, &payload);
utils::log(format!(
"hecatomb: applied config ({} cols, {} rows) to grid {} ({})",
table.columns.len(),
table.gen_count,
index,
if ok { "injected" } else { "no grid api" }
));
view::render();
}