Ejecutar Rust en Wippy
Ejecutar Rust en Wippy
Sección titulada «Ejecutar Rust en Wippy»Compila un componente WebAssembly en Rust y ejecútalo como funciones, comandos CLI y endpoints HTTP.
Qué Vamos a Construir
Sección titulada «Qué Vamos a Construir»Un componente Rust con cuatro funciones exportadas:
- greet - Recibe un nombre, retorna un saludo
- add - Suma dos enteros
- fibonacci - Calcula el n-ésimo número de Fibonacci
- list-files - Lista archivos en un directorio montado
Expondremos estas como funciones invocables, un comando CLI y un endpoint HTTP.
Prerrequisitos
Sección titulada «Prerrequisitos»- Rust toolchain con el target
wasm32-wasip1 - cargo-component
rustup target add wasm32-wasip1cargo install cargo-componentEstructura del Proyecto
Sección titulada «Estructura del Proyecto»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 binaryPaso 1: Crear la Interfaz WIT
Sección titulada «Paso 1: Crear la Interfaz WIT»WIT (WebAssembly Interface Types) define el contrato entre el host y el guest:
Crea demo/wit/world.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;}Cada export se convierte en una función que Wippy puede invocar.
Paso 2: Implementar en Rust
Sección titulada «Paso 2: Implementar en Rust»Crea demo/Cargo.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"Crea demo/src/lib.rs:
#[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);El módulo bindings es generado por cargo-component a partir de la definición WIT.
Paso 3: Compilar el Componente
Sección titulada «Paso 3: Compilar el Componente»cd democargo component build --releaseEsto produce target/wasm32-wasip1/release/demo.wasm. Cópialo a tu aplicación Wippy:
mkdir -p ../app/src/demo/wasmcp target/wasm32-wasip1/release/demo.wasm ../app/src/demo/wasm/demo_component.wasmObtiene el hash SHA-256 para verificación de integridad:
sha256sum ../app/src/demo/wasm/demo_component.wasmPaso 4: Aplicación Wippy
Sección titulada «Paso 4: Aplicación Wippy»Infraestructura
Sección titulada «Infraestructura»Crea app/src/_index.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: demo:gateway prefix: /
- name: processes kind: process.host lifecycle: auto_start: true
- name: terminal kind: terminal.host lifecycle: auto_start: trueFunciones WASM
Sección titulada «Funciones WASM»Crea app/src/demo/wasm/_index.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: inlinePuntos clave:
- Una sola entrada
fs.directoryproporciona el binario WASM - Múltiples funciones referencian el mismo binario con diferentes valores de
method - El campo
hashverifica la integridad del binario al momento de carga - El pool
inlinecrea una instancia nueva por llamada
Funciones con WASI
Sección titulada «Funciones con WASI»La función list-files accede al sistema de archivos, por lo que necesita imports WASI:
- 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: inlineLa sección wasi.mounts mapea una entrada del sistema de archivos de Wippy a una ruta del guest. Dentro del módulo WASM, /data apunta al directorio demo.wasm:assets.
Comandos CLI
Sección titulada «Comandos CLI»Crea app/src/demo/_index.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: /dataEl bloque meta.command registra el proceso como un comando CLI con nombre. El comando greet no necesita imports WASI ya que sólo usa operaciones de strings. El comando ls necesita acceso al sistema de archivos.
Endpoint HTTP
Sección titulada «Endpoint HTTP»Agrega a app/src/demo/wasm/_index.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_greetEl transporte wasi-http mapea el contexto de solicitud/respuesta HTTP a los argumentos y resultados WASM.
Paso 5: Inicializar y Ejecutar
Sección titulada «Paso 5: Inicializar y Ejecutar»cd appwippy initEjecutar Comandos CLI
Sección titulada «Ejecutar Comandos CLI»# List available commandswippy run listAvailable commands: greet Greet someone via WASM ls List files from mounted directory# Run greetwippy run greet# Run ls to list mounted directorywippy run lsEjecutar como Servicio
Sección titulada «Ejecutar como Servicio»wippy runEsto inicia el servidor HTTP en el puerto 8090. Prueba el endpoint:
curl -X POST http://localhost:8090/greetLlamar desde Lua
Sección titulada «Llamar desde Lua»Las funciones WASM se invocan de la misma manera que las funciones 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: 55Siguientes Pasos
Sección titulada «Siguientes Pasos»- Descripción General de WASM - Descripción general del runtime WebAssembly
- Funciones WASM - Referencia de configuración de funciones
- Procesos WASM - Referencia de configuración de procesos
- Funciones Host - Imports WASI disponibles
- Referencia CLI - Documentación de comandos CLI