# Rust WASM _Path: en/tutorials/rust-wasm_ ## Table of Contents - Running Rust on Wippy ## Content # Running Rust on Wippy Build a Rust WebAssembly component and run it as functions, CLI commands, and HTTP endpoints. ## What We're Building A Rust component with four exported functions: - **greet** - Takes a name, returns a greeting - **add** - Adds two integers - **fibonacci** - Computes the nth Fibonacci number - **list-files** - Lists files in a mounted directory We'll expose these as callable functions, a CLI command, and an HTTP endpoint. ## Prerequisites - [Rust toolchain](https://rustup.rs/) with `wasm32-wasip1` target - [cargo-component](https://github.com/bytecodealliance/cargo-component) ```bash rustup target add wasm32-wasip1 cargo install cargo-component ``` ## Project Structure ``` rust-wasm-demo/ ├── demo/ # Rust component │ ├── Cargo.toml │ ├── wit/ │ │ └── world.wit # WIT interface │ └── src/ │ └── lib.rs # Implementation └── app/ # Wippy application ├── wippy.lock └── src/ ├── _index.yaml # Infrastructure └── demo/ ├── _index.yaml # CLI processes └── wasm/ ├── _index.yaml # WASM entries └── demo_component.wasm # Compiled binary ``` ## Step 1: Create the WIT Interface WIT (WebAssembly Interface Types) defines the contract between host and guest: Create `demo/wit/world.wit`: ```wit package component:demo; world demo { export greet: func(name: string) -> string; export add: func(a: s32, b: s32) -> s32; export fibonacci: func(n: u32) -> u64; export list-files: func(path: string) -> string; } ``` Each export becomes a function that Wippy can call. ## Step 2: Implement in Rust Create `demo/Cargo.toml`: ```toml [package] name = "demo" version = "0.1.0" edition = "2024" [dependencies] wit-bindgen-rt = { version = "0.44.0", features = ["bitflags"] } [lib] crate-type = ["cdylib"] [profile.release] opt-level = "s" lto = true [package.metadata.component] package = "component:demo" ``` Create `demo/src/lib.rs`: ```rust #[allow(warnings)] mod bindings; use bindings::Guest; struct Component; impl Guest for Component { fn greet(name: String) -> String { format!("Hello, {}!", name) } fn add(a: i32, b: i32) -> i32 { a + b } fn fibonacci(n: u32) -> u64 { if n <= 1 { return n as u64; } let (mut a, mut b) = (0u64, 1u64); for _ in 2..=n { let next = a + b; a = b; b = next; } b } fn list_files(path: String) -> String { let mut result = String::new(); match std::fs::read_dir(&path) { Ok(entries) => { for entry in entries { match entry { Ok(e) => { let name = e.file_name().to_string_lossy().to_string(); let meta = e.metadata(); let (kind, size) = match meta { Ok(m) => { let kind = if m.is_dir() { "dir" } else { "file" }; (kind, m.len()) } Err(_) => ("?", 0), }; let line = format!("{:<6} {:>8} {}", kind, size, name); println!("{}", line); result.push_str(&line); result.push('\n'); } Err(e) => { let line = format!("error: {}", e); eprintln!("{}", line); result.push_str(&line); result.push('\n'); } } } } Err(e) => { let line = format!("cannot read {}: {}", path, e); eprintln!("{}", line); result.push_str(&line); result.push('\n'); } } result } } bindings::export!(Component with_types_in bindings); ``` The `bindings` module is generated by `cargo-component` from the WIT definition. ## Step 3: Build the Component ```bash cd demo cargo component build --release ``` This produces `target/wasm32-wasip1/release/demo.wasm`. Copy it to your Wippy app: ```bash mkdir -p ../app/src/demo/wasm cp target/wasm32-wasip1/release/demo.wasm ../app/src/demo/wasm/demo_component.wasm ``` Get the SHA-256 hash for integrity verification: ```bash sha256sum ../app/src/demo/wasm/demo_component.wasm ``` ### Infrastructure Create `app/src/_index.yaml`: ```yaml version: "1.0" namespace: demo entries: - name: gateway kind: http.service meta: comment: HTTP server addr: ":8090" lifecycle: auto_start: true - name: api kind: http.router meta: comment: Public API router server: gateway prefix: / - name: processes kind: process.host lifecycle: auto_start: true - name: terminal kind: terminal.host lifecycle: auto_start: true ``` ### WASM Functions Create `app/src/demo/wasm/_index.yaml`: ```yaml version: "1.0" namespace: demo.wasm entries: - name: assets kind: fs.directory meta: comment: Filesystem with WASM binaries directory: ./src/demo/wasm - name: greet_function kind: function.wasm meta: comment: Greet function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet pool: type: inline - name: add_function kind: function.wasm meta: comment: Add function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: add pool: type: inline - name: fibonacci_function kind: function.wasm meta: comment: Fibonacci function via payload transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: fibonacci pool: type: inline ``` Key points: - A single `fs.directory` entry provides the WASM binary - Multiple functions reference the same binary with different `method` values - The `hash` field verifies binary integrity at load time - `inline` pool creates a fresh instance per call ### Functions with WASI The `list-files` function accesses the filesystem, so it needs WASI imports: ```yaml - name: list_files_function kind: function.wasm meta: comment: Filesystem listing with WASI mounts fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: list-files imports: - wasi:cli - wasi:io - wasi:clocks - wasi:filesystem wasi: mounts: - fs: demo.wasm:assets guest: /data pool: type: inline ``` The `wasi.mounts` section maps a Wippy filesystem entry to a guest path. Inside the WASM module, `/data` points to the `demo.wasm:assets` directory. ### CLI Commands Create `app/src/demo/_index.yaml`: ```yaml version: "1.0" namespace: demo.cli entries: - name: greet kind: process.wasm meta: comment: Greet someone via WASM command: name: greet short: Greet someone via WASM fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet - name: ls kind: process.wasm meta: comment: List files from mounted WASI filesystem command: name: ls short: List files from mounted directory fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: list-files imports: - wasi:cli - wasi:io - wasi:clocks - wasi:filesystem wasi: mounts: - fs: demo.wasm:assets guest: /data ``` The `meta.command` block registers the process as a named CLI command. The `greet` command needs no WASI imports since it only uses string operations. The `ls` command needs filesystem access. ### HTTP Endpoint Add to `app/src/demo/wasm/_index.yaml`: ```yaml - name: http_greet kind: function.wasm meta: comment: Greet exposed via wasi-http transport fs: demo.wasm:assets path: /demo_component.wasm hash: sha256:YOUR_HASH_HERE method: greet transport: wasi-http pool: type: inline - name: http_greet_endpoint kind: http.endpoint meta: comment: HTTP POST endpoint for WASM greet router: demo:api method: POST path: /greet func: http_greet ``` The `wasi-http` transport maps HTTP request/response context to WASM arguments and results. ## Step 5: Initialize and Run ```bash cd app wippy init ``` ### Run CLI Commands ```bash # List available commands wippy run list ``` ``` Available commands: greet Greet someone via WASM ls List files from mounted directory ``` ```bash # Run greet wippy run greet ``` ```bash # Run ls to list mounted directory wippy run ls ``` ### Run as a Service ```bash wippy run ``` This starts the HTTP server on port 8090. Test the endpoint: ```bash curl -X POST http://localhost:8090/greet ``` ### Call from Lua WASM functions are called the same way as Lua functions: ```lua local funcs = require("funcs") local greeting, err = funcs.call("demo.wasm:greet_function", "World") -- greeting: "Hello, World!" local sum, err = funcs.call("demo.wasm:add_function", 6, 7) -- sum: 13 local fib, err = funcs.call("demo.wasm:fibonacci_function", 10) -- fib: 55 ``` ## Three Ways to Expose WASM | Approach | Entry Kind | Use Case | |----------|-----------|----------| | Function | `function.wasm` | Called from Lua or other WASM via `funcs.call()` | | CLI Command | `process.wasm` + `meta.command` | Terminal commands via `wippy run ` | | HTTP Endpoint | `function.wasm` + `http.endpoint` | REST API via `wasi-http` transport | All three use the same compiled `.wasm` binary and reference the same methods. ## Building for Other Languages Any language that compiles to the WebAssembly Component Model works with Wippy. Define your WIT interface, implement the exports, compile to `.wasm`, and configure entries in `_index.yaml`. ## See Also - [WASM Overview](wasm/overview.md) - WebAssembly runtime overview - [WASM Functions](wasm/functions.md) - Function configuration reference - [WASM Processes](wasm/processes.md) - Process configuration reference - [Host Functions](wasm/hosts.md) - Available WASI imports - [CLI Reference](guides/cli.md) - CLI command documentation ## Navigation Previous: Authentication (tutorials/auth) Next: LLM Agent (tutorials/llm-agent)