Llamar Rust desde el Frontend
Llamar Rust desde el Frontend
Definir un comando
En src-tauri/src/lib.rs:
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hola, {}!", name)
}
Registralo en el builder:
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error");
}
Invocar desde JavaScript
import { invoke } from '@tauri-apps/api/core';
const mensaje = await invoke('greet', { name: 'Artiko' });
console.log(mensaje); // "Hola, Artiko!"
Los argumentos se pasan como objeto con claves en camelCase. Tauri los convierte automaticamente al snake_case de Rust.
Argumentos multiples
#[tauri::command]
fn login(user: String, password: String) -> String {
format!("Usuario: {}", user)
}
const result = await invoke('login', { user: 'admin', password: '123' });
Manejo de errores
Usa Result para retornar errores:
#[tauri::command]
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("Division por cero".into())
} else {
Ok(a / b)
}
}
try {
const result = await invoke('divide', { a: 10, b: 0 });
} catch (error) {
console.error(error); // "Division por cero"
}
Comandos asincronos
#[tauri::command]
async fn fetch_data(url: String) -> Result<String, String> {
let response = reqwest::get(&url)
.await
.map_err(|e| e.to_string())?;
response.text()
.await
.map_err(|e| e.to_string())
}
Desde JavaScript sigue siendo una promesa normal — no cambia nada.
Acceder al contexto
Estado de la app
struct AppConfig {
api_url: String,
}
#[tauri::command]
fn get_api_url(state: tauri::State<AppConfig>) -> String {
state.api_url.clone()
}
pub fn run() {
tauri::Builder::default()
.manage(AppConfig { api_url: "https://api.ejemplo.com".into() })
.invoke_handler(tauri::generate_handler![get_api_url])
.run(tauri::generate_context!())
.expect("error");
}
Ventana actual
#[tauri::command]
async fn get_label(window: tauri::WebviewWindow) -> String {
window.label().to_string()
}
AppHandle
#[tauri::command]
async fn get_app_dir(app: tauri::AppHandle) -> Result<String, String> {
let dir = app.path().app_data_dir()
.map_err(|e| e.to_string())?;
Ok(dir.to_string_lossy().to_string())
}
Comandos en modulos separados
src-tauri/src/commands.rs:
#[tauri::command]
pub fn my_command() -> String {
"Desde modulo separado".into()
}
src-tauri/src/lib.rs:
mod commands;
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![commands::my_command])
.run(tauri::generate_context!())
.expect("error");
}
Canales para streaming
Para transmitir datos en chunks (progreso de descarga, logs en tiempo real):
use tauri::ipc::Channel;
#[tauri::command]
async fn download(url: String, on_progress: Channel<u32>) {
for i in 0..=100 {
on_progress.send(i).unwrap();
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
}
await invoke('download', {
url: 'https://ejemplo.com/archivo.zip',
onProgress: new Channel((progress) => {
console.log(`${progress}%`);
})
});
Resumen
#[tauri::command]define funciones invocables desde JavaScriptinvoke('nombre', { args })las llama con argumentos en camelCase- Usa
Result<T, String>para errores que se convierten en Promise reject - Accede a estado, ventana y AppHandle como parametros inyectados
- Separa comandos en modulos para mantener
lib.rslimpio - Usa canales para streaming de datos de alto volumen
← Arquitectura | Indice | Siguiente: Llamar al Frontend desde Rust →