zgba Network

Rust WebAssembly with WebForms Core 2.1

What is WebForms Core? WebForms Core is a modern multi-platform web technology from Elanat for building interactive HTML applications through server-defined commands and client-side command execution. WebForms Core uses a server-oriented architecture in which the server defines UI behavior through commands, while a lightweight browser runtime executes those commands. With WebForms Core 2.1 (WFC), WebAssembly can also be used as an execution layer within this architecture. In this article, we will use Rust WebAssembly with WebForms Core and demonstrate two approaches: Rust with wasm-bindgen Raw WebAssembly using extern “C” Both approaches can execute Rust code through WebForms Core. More importantly, Rust code running inside WebAssembly can use the WebForms Core WebForms class to generate WebForms Core commands. This creates a pipeline such as: Rust ↓ WebAssembly ↓ WebForms ↓ WebForms Core Commands ↓ WebFormsJS ↓ Browser DOM The WASM module executes in the browser, while the server-side WebForms class declares when and how the WASM method should be executed. The WASM method can then use the WebForms API to generate WebForms Core commands, which are executed by WebFormsJS in the browser. Rust WebAssembly Rust has first-class support for compiling code to WebAssembly. A Rust project can target: wasm32-unknown-unknown For example: cargo +stable-x86_64-pc-windows-gnu build —release —target wasm32-unknown-unknown The resulting WebAssembly module is generated under: target/wasm32-unknown-unknown/release/ For example: webformscore_wasm_test.wasm This module can then be executed in the browser through WebForms Core’s WebAssembly execution layer. Rust in Crates.io Rust in Crates.io (https://crates.io/crates/webformscore) CLI cargo add webformscore Project settings [dependencies] webformscore = ”#.#.#” Two Rust WebAssembly Approaches WebForms Core can work with different Rust WebAssembly execution models. 1. wasm-bindgen The first approach uses: use wasm_bindgen::prelude::; and: #[wasm_bindgen] The Rust project can then be processed by wasm-bindgen: wasm-bindgen target/wasm32-unknown-unknown/release/webformscore_wasm_test.wasm —target web —out-dir pkg This produces JavaScript glue code and a WebAssembly module. The resulting files include: webformscore_wasm_test.js webformscore_wasm_test_bg.wasm WebForms Core can execute the generated JavaScript module through its JavaScript WASM executor. The wasm-bindgen approach is useful when Rust code needs convenient JavaScript interoperability. 2. Raw WebAssembly The second approach does not use wasm-bindgen. Instead, Rust functions can be exported directly using: #[no_mangle] pub extern “C” fn For example: #[no_mangle] pub extern “C” fn add(a: i32, b: i32) -> i32 { a + b } The function is exported directly into the WebAssembly module. There is no JavaScript glue code generated by wasm-bindgen. The resulting architecture is: Rust ↓ extern “C” ↓ Raw .wasm ↓ WebForms Core WASM Executor This approach is particularly interesting for WebForms Core because the WASM module can expose a direct callable interface without depending on the wasm-bindgen JavaScript layer. Creating the Rust Project Create a Rust library project: cargo new webformscore_wasm_test —lib Enter the project: cd webformscore_wasm_test The project can use: [package] name = “webformscore_wasm_test” version = “2.1.0” edition = “2021” [lib] crate-type = [“cdylib”] [dependencies] webformscore = “2.1.0” The important dependency is: webformscore = “2.1.0” This makes the WebForms Core Rust implementation available inside the WebAssembly project. Using WebForms Core from Rust The Rust code can directly use the WebForms class: use webformscore::WebForms; For example: let mut form = WebForms::new(); form.set_text(“h3Tag”, “Text From Wasm”); form.set_background_color(”-”, “lightgreen”); form.set_font_size(”-”, “30px”); form.response() The Rust code does not directly manipulate the browser DOM. Instead, it creates a WebForms Core response containing commands. This preserves the same command-oriented architecture used by WebForms Core. The Rust WASM module therefore does not need to know how WebFormsJS applies the commands to the DOM. Rust with wasm-bindgen The wasm-bindgen version is straightforward. The complete Rust source can be written as: use wasm_bindgen::prelude::; use webformscore::WebForms; #[wasm_bindgen] pub fn add(a: i32, b: i32) -> i32 { a + b } #[wasm_bindgen] pub fn set_data( input_place: String, text: String, background_color: String, font_size: String, ) -> String { let mut form = WebForms::new(); form.set_text(&input_place, &text); form.set_background_color(”-”, &background_color); form.set_font_size(”-”, &font_size); form.response() } #[wasm_bindgen] pub fn get_html() -> String { “Tag From Wasm!“.to_string() } The important point is that the Rust methods can use: WebForms directly. The WebAssembly method therefore does not need to manipulate the DOM itself. It can generate a WebForms Core response instead. Building the wasm-bindgen Module Build the Rust WebAssembly module: cargo +stable-x86_64-pc-windows-gnu build —release —target wasm32-unknown-unknown Then run: wasm-bindgen targetwasm32-unknown-unknownreleasewebformscore_wasm_test.wasm —target web —out-dir pkg The generated package contains the JavaScript glue required by the wasm-bindgen execution model. The resulting structure is conceptually: webformscore_wasm_test.js webformscore_wasm_test_bg.wasm webformscore_wasm_test.d.ts The JavaScript file provides the JavaScript-facing interface for the Rust WebAssembly module. Raw WebAssembly with Rust Rust can also export functions directly without wasm-bindgen. A simple function is: #[no_mangle] pub extern “C” fn add(a: i32, b: i32) -> i32 { a + b } This function is exported directly by the WebAssembly module. A complete Raw WASM example can be written as: use std::ffi::CStr; use webformscore::WebForms; #[no_mangle] pub extern “C” fn add(a: i32, b: i32) -> i32 { a + b } #[no_mangle] pub extern “C” fn alloc(size: usize) -> *mut u8 { let mut buffer = Vec::with_capacity(size); let ptr = buffer.as_mut_ptr(); std::mem::forget(buffer); ptr } #[no_mangle] pub extern “C” fn set_data( input_place: *const u8, text: *const u8, background_color: *const u8, font_size: *const u8, ) -> *const u8 { let input_place = unsafe { CStr::from_ptr(input_place as *const i8) .to_str() .unwrap() }; let text = unsafe { CStr::from_ptr(text as *const i8) .to_str() .unwrap() }; let background_color = unsafe { CStr::from_ptr(background_color as *const i8) .to_str() .unwrap() }; let font_size = unsafe { CStr::from_ptr(font_size as *const i8) .to_str() .unwrap() }; let mut form = WebForms::new(); form.set_text(input_place, text); form.set_background_color(”-”, background_color); form.set_font_size(”-”, font_size); let response = form.response(); let mut output = response.into_bytes(); output.push(0); Box::into_raw(output.into_boxed_slice()) as *const u8 } #[no_mangle] pub extern “C” fn get_html() -> const u8 { b”Tag From Wasm!0”.as_ptr() } This version has no: use wasm_bindgen::prelude::; and no: #[wasm_bindgen] The functions are exported directly from the WebAssembly module. Passing Strings to Raw WebAssembly Primitive values such as: i32 are straightforward: pub extern “C” fn add(a: i32, b: i32) -> i32 Strings require an explicit memory representation. In the Raw WASM example, WebForms Core passes strings into WebAssembly memory and the Rust code reads them using: CStr::from_ptr(…) For example: let text = unsafe { CStr::from_ptr(text as *const i8) .to_str() .unwrap() }; The Rust method can then use the resulting string normally: form.set_text(input_place, text); The response is returned through a pointer to UTF-8 data. This represents a simple ABI suitable for direct WebAssembly execution. Returning WebForms Core Responses The most interesting part of the Raw WASM example is that set_data does not return a traditional application value. It creates a WebForms Core response: let mut form = WebForms::new(); form.set_text(input_place, text); form.set_background_color(”-”, background_color); form.set_font_size(”-”, font_size); let response = form.response(); The response can then be returned to WebForms Core. Therefore: Rust WASM ↓ WebForms ↓ response() ↓ WebForms Core ↓ WebFormsJS ↓ Browser DOM The Rust WebAssembly method effectively becomes another source of WebForms Core commands. This is the central architectural idea of the example. Using Rust WASM from WebForms Core The server-side WebForms Core controller can declare a WASM method invocation that is executed by the browser. For example: using CodeBehind; public partial class WasmRustController : CodeBehindController { public void PageLoad(HttpContext context) { string WasmPath = “/web-assembly/rust/webformscore_wasm_test_bg.wasm”; WebForms form = new WebForms(); form.AddText( "", Fetch.WasmMethod( WasmLanguage.Rust, WasmPath, “add”, [10000, 3] ) ); form.SetWasmEvent( “WasmEvent”, HtmlEvent.OnClick, WasmLanguage.Rust, WasmPath, “setData”, [“h3Tag”, “Text From Wasm”, “lightgreen”, “30px”] ); form.SetWasmEvent( “WasmEventWithOutput”, HtmlEvent.OnClick, WasmLanguage.Rust, WasmPath, “getHtml”, [], “WasmHtmlOutput” ); Write(form.ExportToHtmlComment()); } } Both the wasm-bindgen and Raw WebAssembly examples use server-side WebForms Core code to demonstrate the power of its server-oriented architecture. However, server-side orchestration is not required by WebForms Core itself. These scenarios can also be configured to run without server-side WebForms code when server orchestration is not needed. The same WFC API can therefore declare Rust WASM method calls. The server does not need to know the internal implementation of the Rust method. It only needs to specify: WASM language WASM path method name arguments The server defines the execution behavior, while the browser executes the WASM module and processes the resulting WebForms Core response. Three Rust WASM Methods The example exposes three methods: add set_data get_html The first method returns a number: add(10000, 3) which produces: 10003 The second method receives strings and generates a WebForms Core response: set_data( “h3Tag”, “Text From Wasm”, “lightgreen”, “30px” ) The third method returns HTML: get_html() which produces: Tag From Wasm! This demonstrates that WebForms Core can use WASM methods for different kinds of operations: returning values generating WebForms Core commands returning HTML HTML The HTML remains standard HTML: @page @controller WasmRustController @layout “/layout.aspx” @{ ViewData.Add(“title”, “Rust Wasm”); }

Rust Wasm

Rust WASM Result:

Wasm Tag Changing!

Wasm Html Output

There is no custom WebAssembly HTML element. There is no special component syntax. There is no Rust-specific DOM layer. The page remains standard HTML. The WASM Method as a Command Execution Source Consider this: form.SetWasmEvent( “WasmEvent”, HtmlEvent.OnClick, WasmLanguage.Rust, WasmPath, “setData”, [“h3Tag”, “Text From Wasm”, “lightgreen”, “30px”] ); WebForms Core declares that when the button is activated, a Rust WASM method should be executed. The Rust method can then create: let mut form = WebForms::new(); form.set_text(input_place, text); form.set_background_color(”-”, background_color); form.set_font_size(”-”, font_size); and return the WebForms Core response. The WASM method therefore becomes an execution source for WebForms Core commands. The browser does not need a separate Rust-specific UI architecture. WASM Event With Output The same mechanism can return HTML: #[no_mangle] pub extern “C” fn get_html() -> *const u8 { b”Tag From Wasm!0”.as_ptr() } The server connects it to an output element: form.SetWasmEvent( “WasmEventWithOutput”, HtmlEvent.OnClick, WasmLanguage.Rust, WasmPath, “getHtml”, [], “WasmHtmlOutput” ); The returned HTML is then placed into:

Wasm Html Output

This demonstrates that a Rust WASM method can also be used as a source of dynamic UI output. One WebForms Core API, Different WASM Models The WebForms Core API does not have to change when the WASM execution model changes. The application can use: Rust + wasm-bindgen or: Rust + Raw WebAssembly while the WebForms Core invocation remains conceptually the same: WasmMethod The execution layer handles the differences between the WASM models. This gives WebForms Core a unified way to work with different WebAssembly environments. wasm-bindgen vs Raw WebAssembly The two approaches have different characteristics. Feature wasm-bindgen Raw WebAssembly Rust API High-level Low-level String handling Mostly abstracted Explicit memory handling JavaScript glue Generated No wasm-bindgen glue .js module Yes No Direct .wasm loading Possible with a compatible interface Yes ABI complexity Mostly handled by wasm-bindgen Developer-defined Memory/ABI control Mostly abstracted Developer-defined WebForms Core integration Yes Yes Best suited for General Rust WASM applications Direct WASM execution The wasm-bindgen approach is convenient when Rust needs a rich JavaScript interoperability layer. The Raw WebAssembly approach is attractive when the goal is a directly callable WASM module with a developer-defined ABI. Neither approach changes the WebForms Core command model. WebForms Core Rust WASM Architecture The resulting architecture can be viewed as: Server │ │ WebForms ↓ WasmMethod │ ↓ Browser Runtime │ ┌────────┴────────┐ │ │ ↓ ↓ wasm-bindgen Raw WASM │ │ ↓ ↓ Rust WASM Rust WASM │ │ └────────┬────────┘ ↓ WebForms │ ↓ WebForms Core Response │ ↓ WebFormsJS │ ↓ HTML DOM This shows the role of WebAssembly more clearly. WebAssembly does not replace the WebForms Core execution model. Instead, it provides another execution environment inside that model. No Separate JavaScript Business Logic One of the interesting properties of this example is that application behavior can be implemented in Rust without creating a separate JavaScript business-logic layer. The Rust code contains the behavior: let mut form = WebForms::new(); form.set_text(input_place, text); form.set_background_color(”-”, background_color); form.set_font_size(”-”, font_size); form.response() The HTML contains standard HTML: And WebForms Core connects the two: form.SetWasmEvent( “WasmEvent”, HtmlEvent.OnClick, WasmLanguage.Rust, WasmPath, “setData”, […] ); The Rust code does not need to search for the button, register a DOM event listener, or manually update the target element. WebForms Core defines the interaction, while WebFormsJS executes the resulting commands in the browser. Raw WebAssembly and wasm-bindgen Are Different It is important to distinguish these two approaches. A Raw WebAssembly module can expose functions directly: module.wasm ↓ WebAssembly.instantiate() ↓ exported function The wasm-bindgen approach introduces a generated JavaScript interoperability layer: Rust ↓ wasm-bindgen ↓ generated JavaScript ↓ _bg.wasm ↓ Rust exported function These are different execution models. WebForms Core can provide an abstraction over both models while keeping the application-level WASM invocation consistent. WebAssembly as an Execution Layer This example demonstrates a broader idea. WebAssembly does not necessarily have to represent an entire frontend application. A WASM module can instead provide a specific execution capability. For example: WebForms Core ↓ Rust WASM ↓ Process data ↓ Generate WebForms Core response ↓ WebFormsJS ↓ Browser DOM The Rust code can therefore perform computation, generate UI commands, or return values without becoming a complete client-side application framework. This allows WebAssembly to participate in the existing WebForms Core command architecture. Rust and WebForms Core Rust is particularly interesting in this architecture because it can be compiled into WebAssembly while still using the WebForms Core API. For example: let mut form = WebForms::new(); form.set_text(“h3Tag”, “Text From Wasm”); The Rust code does not need to know how WebFormsJS internally executes the resulting command. It only creates the WebForms Core response. This separates: Application execution from: Browser UI execution The WASM module performs its computation and creates the desired WebForms Core response, while WebFormsJS remains responsible for applying commands to the HTML DOM. Rust WASM Compared with Direct JavaScript DOM Manipulation A conventional JavaScript approach might directly perform: document.getElementById(“h3Tag”).textContent = “Text From Wasm”; The Rust WebAssembly approach demonstrated here does not need to manipulate the DOM directly. Instead, it can produce WebForms Core behavior through: WebForms This means the Rust code participates in the same command architecture used by the rest of the WebForms Core application. The UI execution remains centralized in the WebForms Core command pipeline. WebForms Core 2.1 WebForms Core 2.1 extends the WebForms Core execution model with WebAssembly integration. Rust is particularly interesting for this capability because it supports both: wasm-bindgen and: Raw WebAssembly The same WebForms Core application can therefore use Rust WASM as an execution layer while keeping its HTML and UI command model intact. The important point is not simply that: Rust can run in the browser. The more interesting part is: Rust running in WebAssembly can participate in the WebForms Core command architecture. A WASM module can therefore become a computational or UI-command-producing execution environment without requiring the application to adopt a separate frontend architecture. Final Architecture The complete architecture can be summarized as: Server │ ↓ WebForms │ ↓ WasmMethod │ ↓ Browser │ ┌────────┴────────┐ │ │ ↓ ↓ wasm-bindgen Raw WebAssembly │ │ └────────┬────────┘ ↓ Rust │ ↓ WebForms │ ↓ WebForms Core Response │ ↓ WebFormsJS │ ↓ HTML DOM This makes Rust WebAssembly more than a way to run Rust code in the browser. It becomes another execution environment capable of participating in the WebForms Core server-orchestrated command architecture. The same model can therefore combine: Rust WebAssembly Raw WebAssembly wasm-bindgen WebForms Core WebForms Core commands WebFormsJS Standard HTML without requiring the application to become a traditional JavaScript SPA. Conclusion Rust WebAssembly and WebForms Core provide an interesting combination. With wasm-bindgen, Rust can expose a convenient WebAssembly API with JavaScript interoperability. With Raw WebAssembly, Rust can expose functions directly through extern “C” using a developer-defined ABI. Both approaches can participate in the WebForms Core execution model. The result is a different way of thinking about WebAssembly: WebAssembly does not have to replace the UI architecture. It can become an execution layer inside the UI architecture. When that execution layer can directly use WebForms, Rust code can generate WebForms Core commands instead of implementing a separate client-side DOM architecture. This extends the WebForms Core execution model from: Server ↓ WebForms ↓ Commands ↓ WebFormsJS ↓ HTML DOM to also support: Server ↓ WebForms ↓ WasmMethod ↓ Rust WASM ↓ WebForms ↓ Commands ↓ WebFormsJS ↓ HTML DOM This is one of the capabilities introduced by WebForms Core 2.1. Related Links WebForms Core WebFormsJS WebForms Core GitHub

View original article