Saltar al contenido

La conversión de nombres

Los argumentos cambian de convención al cruzar y el nombre del comando no. Qué se convierte exactamente, cómo desactivarlo por comando, y por qué esta regla asimétrica produce el segundo fallo más frecuente del framework.

La regla, en una tabla

Las dos últimas filas son la parte que nadie espera: Tauri convierte los argumentos de primer nivel, y dentro de un struct manda serde. Es decir, esto no es simétrico:

Rust
#[tauri::command]async fn buscar(termino_de_busqueda: String, filtro: Filtro) { }//               ^ Tauri lo convierte              ^ y dentro de esto, no#[derive(Deserialize)]struct Filtro {    incluir_archivados: bool,   // llega como incluir_archivados}
JS
await invoke('buscar', {  terminoDeBusqueda: 'x',              // convertido por Tauri  filtro: { incluir_archivados: true } // NO convertido: es serde});

La corrección: decidir de una vez por tipo

Mezclar dos convenciones en el mismo mensaje es una fuente inagotable de errores. La forma de acabar con ello es poner el atributo de renombrado en todos los tipos que cruzan, sin excepción:

Rust
#[derive(Serialize, Deserialize)]#[serde(rename_all = "camelCase")]   // en TODOS los tipos que cruzanpub struct Filtro {    pub incluir_archivados: bool,    // cruza como incluirArchivados}

Con eso, el mensaje entero habla una sola convención y la regla mental es sencilla: en Rust todo va en su estilo, y al cruzar todo se convierte. Salvo el nombre del comando, que sigue siendo literal.

Y la alternativa opuesta, igual de válida: desactivar la conversión de Tauri por comando, y que todo viaje con el nombre de Rust.

Rust
#[tauri::command(rename_all = "snake_case")]async fn buscar(termino_de_busqueda: String) { }
JS
await invoke('buscar', { termino_de_busqueda: 'x' });

Lo que no funciona es no elegir. La única regla mala es la implícita.

El error

TXT
invalid args `terminoDeBusqueda` for command `buscar`: command buscar missing required key terminoDeBusqueda

Es del tramo 6 del recorrido, y tiene una virtud: dice el nombre del campo que esperaba. Leerlo entero suele resolver el problema en diez segundos, y es lo que la gente no hace porque el mensaje es largo.

Por qué esto deja de importar

Con bindings generados —nivel 19— todo este nivel se vuelve historia. La función que llamas desde TypeScript ya trae los nombres correctos, en la convención correcta, y el editor te los autocompleta:

TypeScript
// Los nombres los puso el generador desde el struct de Rust. No hay nada que recordar.await commands.buscar('x', { incluirArchivados: true });

Es la misma conclusión que el nivel 14.2: el problema no se arregla recordándolo mejor, se arregla generando el contrato desde un solo sitio. Mientras tanto, la regla es esta y conviene tenerla escrita.