Skip to main content

nodejs/
rust_ffi.rs

1//! JavaScript wiring for inline Rust FFI (`rust { ... }` blocks).
2//!
3//! The heavy lifting lives in fusevm: [`fusevm::RustSugar`] scans and rewrites
4//! the block at the source level, and [`fusevm::ffi`] compiles/loads/marshals
5//! it. This module only supplies the JS-flavored [`fusevm::RustSugar`] config
6//! and the desugar entry the parser calls. The emitted `__rust_compile(...)`
7//! call and every exported bareword are resolved in [`crate::host::call_named`].
8
9use fusevm::RustSugar;
10
11/// Emit the JS statement a `rust { ... }` block desugars to: a call to the
12/// `__rust_compile` builtin carrying the base64-encoded block body and its line.
13/// base64's alphabet (`A-Za-z0-9+/=`) needs no escaping inside the double-quoted
14/// JS string literal.
15fn emit(b64: &str, line: usize) -> String {
16    format!("__rust_compile(\"{b64}\", {line})")
17}
18
19/// JavaScript desugar config: C-family braces with `//` and `/* */` comments.
20/// `newline_boundary` is `true` so a top-level `rust { ... }` on its own line is
21/// recognized — `rust {` is never valid JS otherwise, so this only ever matches
22/// an intended FFI block. The desugar runs on raw source BEFORE lexing, so the
23/// block is replaced in place by a `__rust_compile(...)` expression statement.
24pub const SUGAR: RustSugar = RustSugar {
25    keyword: "rust",
26    line_comments: &["//"],
27    block_comment: Some(("/*", "*/")),
28    newline_boundary: true,
29    emit,
30};
31
32/// Rewrite every top-level `rust { ... }` block in JS source into a
33/// `__rust_compile(...)` call, before lexing. No-op when the source has no
34/// `rust` token.
35pub fn desugar(src: &str) -> String {
36    SUGAR.desugar(src)
37}
38
39#[cfg(test)]
40mod tests {
41    #[test]
42    fn desugars_top_level_block() {
43        let src =
44            "rust { pub extern \"C\" fn add(a: i64, b: i64) -> i64 { a + b } }\nconsole.log(add(2, 3))\n";
45        let out = super::desugar(src);
46        assert!(out.contains("__rust_compile("), "no builtin call: {out}");
47        assert!(!out.contains("pub extern"), "Rust body leaked: {out}");
48        assert!(
49            out.contains("console.log(add(2, 3))"),
50            "trailing code lost: {out}"
51        );
52    }
53
54    #[test]
55    fn leaves_ordinary_js_untouched() {
56        let src = "const x = \"hi\".length;\nconsole.log(x);\n";
57        assert_eq!(super::desugar(src), src);
58    }
59}