Pular para conteúdo

Geração automática da boundary IPC

Status: proposta, não implementada.

Contexto

A boundary IPC do Slot2Sync exige sincronização manual de três lugares a cada mudança de struct, enum ou evento: a struct Rust (#[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")]), a interface TypeScript espelho em src/types/ipc.ts, e o wrapper tipado de invoke() em src/lib/ipc.ts. Não há nenhuma verificação em tempo de compilação — uma divergência produz undefined silencioso em runtime, detectável só por teste manual ou crash na UI (ver Referência — Boundary IPC e a decisão "Tipos compartilhados espelhados manualmente" em Decisões técnicas).

A boundary cresceu bastante desde a análise original que embasa esta decisão: dezenas de comandos hoje (confira src-tauri/src/commands.rs), o que torna o custo do espelhamento manual mais alto do que quando a prioridade "média-baixa" abaixo foi atribuída — vale reavaliar a urgência à luz do tamanho atual.

Duas alternativas para o mesmo objetivo

Ambas visam eliminar a manutenção manual de src/types/ipc.ts, gerando-o a partir das structs Rust. Nenhuma foi adotada ainda.

ts-rs

Derive macro que gera um .d.ts/.ts por tipo anotado (#[derive(TS)]), sem integração direta com o Builder do Tauri — só gera tipos, não comandos nem eventos. Mencionado anteriormente em decisoes-tecnicas.md como "evolução possível" antes desta análise mais aprofundada existir.

tauri-specta

Plugin para Tauri que usa a crate specta para inspecionar tipos Rust em tempo de compilação e gerar comandos, eventos e tipos tipados de uma vez — mais integrado ao ciclo de vida do Builder que o ts-rs puro.

Ganhos de qualquer uma das duas: drift torna-se impossível por construção (hoje é detectado em runtime, não em build); menor custo por comando/tipo novo (deixa de exigir edição manual do lado TS); eventos e seus payloads também podem ser gerados (tauri-specta cobre isso nativamente; ts-rs cobriria só os tipos de payload).

Perdas/custos comuns: dependência nova com histórico mais curto que o próprio Tauri (mitigável — o projeto já usa outros plugins de terceiros, como tauri-plugin-autostart e tauri-plugin-single-instance); todas as structs/enums da boundary precisam do derive adicional (#[derive(specta::Type)] ou #[derive(TS)]) — trabalho de migração, não de manutenção contínua; src/types/ipc.ts deixa de ser editável à mão (qualquer customização precisa ir para o lado Rust); src/lib/ipc.ts (os wrappers de invoke()) continua manual em ambos os casos — tauri-specta reduz mais essa fricção por gerar as assinaturas dos comandos também, não só os tipos; AppErrorPayload.code é um caso especial (o shape { code, message, detail } é construído à mão em error.rs via Serialize customizado) e precisaria ser revisitado para ficar compatível com geração automática, em qualquer das duas ferramentas.

Esforço de migração estimado

Adicionar a dependência (Cargo.toml); anotar as structs/enums da boundary com o derive escolhido; ajustar AppError para gerar corretamente (ou manter o union code manual com teste de regressão); criar o passo de geração e integrá-lo ao CI antes do npm run build; remover src/types/ipc.ts do controle manual (gerado) e ajustar o .gitignore se for o caso. Estimativa original: 4–8 horas de trabalho mecânico — sem risco de regressão funcional, é infraestrutura de tipos.

Recomendação

Vale adotar uma das duas — a escolha entre ts-rs (mais simples, só tipos) e tauri-specta (mais completo, cobre comandos/eventos também) ainda não foi feita. Dado o tamanho atual da boundary, a prioridade deveria subir de "média-baixa" (avaliação original) para algo a resolver nas próximas mudanças estruturais da boundary, não indefinidamente adiado — cada novo emulador ou feature adiciona comandos, e o custo da manutenção manual só cresce. O momento ideal para migrar continua sendo o início de uma sprint de feature nova, para amortizar o esforço junto com as anotações dos tipos novos.