Skip to content

Instantly share code, notes, and snippets.

@saolsen
Last active December 26, 2023 00:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saolsen/d273bb1baba5e912e4dc2b187511affa to your computer and use it in GitHub Desktop.
Save saolsen/d273bb1baba5e912e4dc2b187511affa to your computer and use it in GitHub Desktop.
Rust Val

Make a Val with rust.

Build a https://www.val.town/ val with rust. This is an example of how to compile a rust file to wasm and use it as a val.

Requires rust, wasm-pack and deno.

Usage

  • Put your code in val.rs.
  • Build it.
    • wasm-pack build --target nodejs
    • deno run --allow-read --allow-write package.ts
  • Copy the code in val.ts to your val.

Val made from this gist

Val that uses it

Another example that is an http val.

[package]
name = "rust_val"
version = "0.1.0"
edition = "2018"
[lib]
name="rust_val"
path="val.rs"
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.84"
console_error_panic_hook = { version = "0.1.7", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[profile.release]
opt-level = "s"
import * as base64 from "https://deno.land/std@0.207.0/encoding/base64.ts";
const decoder = new TextDecoder("utf-8");
const cargo_file = decoder.decode(await Deno.readFile("./Cargo.toml"));
if (cargo_file === null) {
throw new Error("Cargo.toml not found");
}
const cargo_package = /\[package\]\nname = "(.*?)"/.exec(cargo_file);
if (cargo_package === null) {
throw new Error("Cargo.toml is invalid");
}
const cargo_package_name = cargo_package[1];
const name = cargo_package_name.replace(/-/g, "_");
const bytes = await Deno.readFile(`./pkg/${name}_bg.wasm`);
if (bytes === null) {
throw new Error(`pkg/${name}_bg.wasm not found`);
}
const encoded = base64.encodeBase64(bytes);
const js_file = decoder.decode(await Deno.readFile(`./pkg/${name}.js`));
if (js_file === null) {
throw new Error(`pkg/${name}.js not found`);
}
const patched =
"" +
`import * as base64 from "https://deno.land/std/encoding/base64.ts"\n\n` +
`const bytes = base64.decodeBase64(\n` +
` // base64 encoded wasm module\n` +
` ${JSON.stringify(encoded)}\n` +
`);\n\n` +
js_file
.replace("let imports = {};", "let imports: any = {};")
// use global TextDecoder TextEncoder
.replace("require(`util`)", "globalThis")
// attach to `imports` instead of module.exports
.replace("= module.exports", "= imports")
.replace(/\nmodule\.exports\.(.*?)\s+/g, "\nexport const $1 = imports.$1 ")
.replace(/$/, "export default imports")
// inline bytes Uint8Array
.replace(/\nconst path.*\nconst bytes.*\n/, "");
const encoder = new TextEncoder();
await Deno.writeFile(`./val.ts`, encoder.encode(patched));
pub fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}
mod util;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub fn say_hello() {
log("Hello from Rust, it works!");
}
#[wasm_bindgen]
pub fn add(x: i32, y: i32) -> i32 {
x + y
}
import * as base64 from "https://deno.land/std/encoding/base64.ts";
const bytes = base64.decodeBase64(
// base64 encoded wasm module
"AGFzbQEAAAABDwNgAn9/AGAAAGACf38BfwI3ARhfX3diaW5kZ2VuX3BsYWNlaG9sZGVyX18aX193YmdfbG9nXzA4ODUwZDZkNGQ5MWY0YzkAAAMDAgECBQMBABEHHAMGbWVtb3J5AgADYWRkAAIJc2F5X2hlbGxvAAEKFQILAEGAgMAAQRoQAAsHACAAIAFqCwsjAQBBgIDAAAsaSGVsbG8gZnJvbSBSdXN0LCBpdCB3b3JrcyEAbwlwcm9kdWNlcnMCCGxhbmd1YWdlAQRSdXN0AAxwcm9jZXNzZWQtYnkDBXJ1c3RjHTEuNzIuMCAoNTY4MGZhMThmIDIwMjMtMDgtMjMpBndhbHJ1cwYwLjIwLjMMd2FzbS1iaW5kZ2VuBjAuMi44OQAsD3RhcmdldF9mZWF0dXJlcwIrD211dGFibGUtZ2xvYmFscysIc2lnbi1leHQ="
);
let imports: any = {};
imports["__wbindgen_placeholder__"] = imports;
let wasm;
const { TextDecoder } = globalThis;
let cachedTextDecoder = new TextDecoder("utf-8", {
ignoreBOM: true,
fatal: true,
});
cachedTextDecoder.decode();
let cachedUint8Memory0 = null;
function getUint8Memory0() {
if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
/**
*/
export const say_hello = (imports.say_hello = function () {
wasm.say_hello();
});
/**
* @param {number} x
* @param {number} y
* @returns {number}
*/
export const add = (imports.add = function (x, y) {
const ret = wasm.add(x, y);
return ret;
});
export const __wbg_log_08850d6d4d91f4c9 = (imports.__wbg_log_08850d6d4d91f4c9 =
function (arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1));
});
const wasmModule = new WebAssembly.Module(bytes);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
wasm = wasmInstance.exports;
export const __wasm = (imports.__wasm = wasm);
export default imports;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment