Entrada (esquema .proto)

Salida (Rust)

Qué hace esta herramienta

Tienes un esquema de Protocol Buffers y un servicio o cliente en Rust que necesita hablar con él. El camino habitual es prost (o tonic para gRPC) cableado en un script build.rs — sólido, pero excesivo cuando solo quieres bocetar los tipos y verificar las formas contra tu código. Este conversor convierte el esquema en definiciones de struct dentro de tu navegador. Pega, copia, suelta en src/types.rs, sigue avanzando.

La salida usa #[derive(Debug, Default, Clone, Serialize, Deserialize)] en cada struct para que haga round-trip por JSON vía serde, y enums con #[repr(i32)] donde la variante de valor cero está marcada con #[default] para que Default::default() coincida con la semántica de proto3. Los mensajes anidados singulares pasan a ser Option<T> — Rust requiere nulabilidad explícita, y eso encaja con lo que prost genera. Vec<T> para campos repeated y HashMap<K, V> para maps llevan ambos #[serde(default)] para que los campos faltantes deserialicen a contenedores vacíos en lugar de fallar.

El mapeo de tipos sigue la especificación de mapeo JSON de proto3: string/bytesString/Vec<u8>, los distintos enteros con tamaño se mapean a i32/i64/u32/u64, y los flotantes caen en f32/f64. Los nombres de campo se convierten a snake_case (que ya es la convención de Rust, así que la mayoría pasan sin tocar), y las variantes de enum pierden el prefijo en mayúsculas a gritos — ORDER_STATUS_UNSPECIFIED en el enum OrderStatus se convierte en Unspecified, tal como lo escribirías a mano. La conversión corre íntegramente en cliente; nada de tu esquema sale de la página.

Cómo usarlo

Tres pasos. La salida está lista para pegar en cualquier crate de Rust que tenga serde en sus dependencias.

1

Pega tu esquema .proto

Suelta el esquema en el editor de la izquierda. syntax = "proto3"; al principio es opcional — el parser maneja bloques anidados de message, declaraciones enum, oneof, map<K, V> y opciones de campo. Los imports se reconocen pero se omiten, así que pega los tipos importados en línea si dependes de ellos.

Los nombres de campo siguen las convenciones de Rust automáticamente: order_id se queda en order_id, shippingAddress (si tienes algo en camelCase en tu esquema) pasa a shipping_address. Los nombres de struct y enum mantienen PascalCase.

2

Lee la salida

A la derecha: una lista plana de declaraciones pub struct y pub enum. Los enums van primero, luego los messages en orden de declaración, con los tipos anidados antes que sus padres. La línea use serde::{Deserialize, Serialize}; siempre está; use std::collections::HashMap; solo aparece si tu esquema tiene campos map.

3

Suelta en tu crate

Pega la salida en un archivo de módulo. Mientras tengas serde = { version = "1", features = ["derive"] } y serde_json en tu Cargo.toml, compila. A partir de ahí es Rust normal — cabléalo a un cliente reqwest, a un Cloudflare Worker, a un handler de axum, a donde necesites acceso tipado a los datos del mensaje.

Cuándo te ahorra tiempo de verdad

Bocetar tipos para un nuevo servicio Rust

Estás construyendo un servicio Rust que consume un endpoint gRPC-gateway, pero no quieres montar un build.rs completo con prost todavía. Pega el .proto, suelta los structs en types.rs, decodifica la respuesta JSON, lanza el prototipo.

Revisar un cambio de API en Protobuf

Un compañero del backend añadió campos a un mensaje. Quieres ver cómo cambia la forma en Rust sin reconstruir todo el crate. Pega el nuevo .proto, haz diff de la salida contra tus tipos existentes, deja un comentario de revisión bien enfocado.

Comparar contra el código generado por prost

Tu build usa prost con reglas type_attribute personalizadas y quieres una referencia limpia de cómo lucen los tipos sin modificar. Pega el esquema aquí para verlo lado a lado. Útil cuando planificas cambios de atributos o migras a otro pipeline de codegen.

CLIs y scripts puntuales

Estás escribiendo un pequeño CLI en Rust que llama a una API JSON respaldada por Protobuf. Cablear prost solo para una herramienta de 100 líneas es más ceremonia de la que pide el trabajo. Toma los structs de aquí, deriva serde, lanza el binario.

Preguntas frecuentes

¿Mi esquema se envía a algún sitio?

No. El parser y el emisor de Rust corren ambos como JavaScript en tu navegador. Abre las DevTools, mira la pestaña Network mientras pegas — cero peticiones. Útil cuando tu esquema incluye nombres de tipos internos, paths de paquete o cualquier cosa que no querrías mandar a un servicio de terceros.

¿Por qué los mensajes anidados van envueltos en Option?

Rust no tiene referencias anulables como Java o Go — cada valor está presente o usas Option<T> para hacer la ausencia explícita. Los submensajes singulares en proto3 son anulables a nivel de cable (la etiqueta de campo puede faltar entera), así que la forma idiomática en Rust es Option<Address>, no Address. Esto coincide con lo que prost genera. Los escalares y enums no van envueltos porque sus valores cero (cadena vacía, 0, false, la variante de enum #[default]) son valores válidos de Rust.

¿Por qué los enteros de 64 bits están tipados como i64/u64 en lugar de strings?

En Rust no hay problema de precisión — i64 y u64 son tipos de primera clase, a diferencia de JavaScript donde los valores de 64 bits son strings. Si estás deserializando JSON producido por un encoder estricto de proto3 JSON, los valores llegarán como strings; serde no los convierte automáticamente. Añade #[serde(with = "serde_with::DisplayFromStr")] o un deserializador personalizado en esos campos si te topas con ese caso.

¿Por qué HashMap en vez de BTreeMap?

HashMap coincide con lo que prost emite para campos map<K, V> y es el default correcto — los campos map de proto no tienen orden. Si necesitas un orden de iteración estable, cambia el tipo a BTreeMap a mano; el resto del código no necesita cambiar.

¿Debería usar esta salida con prost o tonic?

Estos son structs derivados de serde puro, no estructuras codificadas por prost. Hacen round-trip por JSON de proto3 vía serde_json, pero no codifican/decodifican el formato binario de cable. Si necesitas codificación binaria de protobuf, ejecuta prost en un paso build.rs. Si necesitas gRPC, usa tonic. Este conversor es para el caso de JSON-sobre-HTTP (gRPC-gateway, Connect, JSON transcoding).

¿Maneja oneof?

Cada campo de oneof se emite como un campo de struct normal. La salida no impone la restricción de "exactamente uno" que implica oneof — para eso necesitarías un enum de Rust con una variante por caso, lo que depende de la semántica de tu runtime. Si necesitas un manejo estricto de oneof, edita la salida a mano en un pub enum.

Herramientas relacionadas

Si trabajas con Protobuf, JSON y Rust, estas combinan bien: