Entrada (esquema .proto)

Saída (Rust)

O que esta ferramenta faz

Você tem um esquema Protocol Buffers e um serviço ou cliente Rust que precisa falar com ele. O caminho padrão é prost (ou tonic para gRPC) plugado num script build.rs — sólido, mas exagero quando você só quer esboçar os tipos e conferir as formas contra o seu código. Este conversor transforma o esquema em definições de struct dentro do seu navegador. Cole, copie, jogue em src/types.rs, siga em frente.

A saída usa #[derive(Debug, Default, Clone, Serialize, Deserialize)] em cada struct para que ela faça round-trip por JSON via serde, e enums #[repr(i32)] com a variante de valor zero marcada com #[default], de modo que Default::default() bate com a semântica do proto3. Mensagens aninhadas singulares viram Option<T> — Rust exige nulabilidade explícita, e isso bate com o que o prost gera. Vec<T> para campos repeated e HashMap<K, V> para maps recebem ambos #[serde(default)], então campos ausentes desserializam para containers vazios em vez de quebrar.

O mapeamento de tipos segue a spec de mapeamento JSON do proto3: string/bytesString/Vec<u8>, os vários inteiros dimensionados mapeiam para i32/i64/u32/u64, e os floats caem em f32/f64. Os nomes de campo viram snake_case (já é a convenção do Rust, então a maioria passa intocada), e as variantes de enum perdem o prefixo gritante em SCREAMING_SNAKE — ORDER_STATUS_UNSPECIFIED no enum OrderStatus vira Unspecified, do jeito que você escreveria à mão. A conversão roda inteira no cliente; nada do seu esquema sai da página.

Como usar

Três passos. A saída fica pronta para colar em qualquer crate Rust que tenha serde nas dependências.

1

Cole seu esquema .proto

Solte o esquema no editor da esquerda. syntax = "proto3"; no topo é opcional — o parser lida com blocos message aninhados, declarações enum, oneof, map<K, V> e opções de campo. Imports são reconhecidos mas pulados, então cole tipos importados inline se você depender deles.

Os nomes de campo seguem as convenções do Rust automaticamente: order_id continua order_id, shippingAddress (se você tiver algum camelCase no seu esquema) vira shipping_address. Os nomes de struct e enum permanecem em PascalCase.

2

Leia a saída

À direita: uma lista plana de declarações pub struct e pub enum. Os enums vêm primeiro, depois as messages na ordem de declaração, com tipos aninhados antes dos pais. A linha use serde::{Deserialize, Serialize}; está sempre lá; use std::collections::HashMap; só aparece se o seu esquema tem campos map.

3

Solte no seu crate

Cole a saída num arquivo de módulo. Desde que você tenha serde = { version = "1", features = ["derive"] } e serde_json no seu Cargo.toml, compila. Daí em diante é Rust normal — pendure num cliente reqwest, num Cloudflare Worker, num handler axum, em qualquer lugar onde precise de acesso tipado aos dados da mensagem.

Quando isso realmente economiza tempo

Esboçar tipos para um novo serviço Rust

Você está construindo um serviço Rust que consome um endpoint gRPC-gateway, mas ainda não quer montar um build.rs completo com prost. Cole o .proto, jogue os structs em types.rs, decodifique a resposta como JSON, embarque o protótipo.

Revisar uma mudança de API Protobuf

Um colega de backend adicionou campos a uma message. Você quer ver como a forma em Rust vai mudar sem reconstruir o crate inteiro. Cole o novo .proto, dê diff da saída contra os seus tipos atuais, deixe um comentário de revisão pontual.

Conferir o código gerado pelo prost

Seu build usa prost com regras type_attribute customizadas e você quer uma referência limpa de como ficam os tipos sem modificações. Cole o esquema aqui para ver lado a lado. Útil quando você está planejando mudanças de atributos ou migrando para outro pipeline de codegen.

Ferramentas CLI e scripts pontuais

Você está escrevendo uma CLI Rust pequena que chama uma API JSON apoiada em Protobuf. Cabular prost só para uma ferramenta de 100 linhas é cerimônia demais para o tamanho do trabalho. Pegue os structs daqui, derive serde, embarque o binário.

Perguntas comuns

Meu esquema é enviado para algum lugar?

Não. O parser e o emissor Rust rodam ambos como JavaScript no seu navegador. Abra o DevTools, fique de olho na aba Network enquanto cola — zero requisições. Útil quando seu esquema inclui nomes de tipos internos, paths de pacote ou qualquer coisa que você não mandaria para um serviço de terceiros.

Por que mensagens aninhadas vêm envolvidas em Option?

Rust não tem referências nuláveis como Java ou Go — ou o valor está presente, ou você usa Option<T> para tornar a ausência explícita. Submensagens singulares no proto3 são nuláveis no fio (a tag do campo pode estar ausente por completo), então a forma idiomática em Rust é Option<Address>, não Address. Isso bate com o que o prost gera. Escalares e enums não são envolvidos porque seus valores zero (string vazia, 0, false, a variante de enum #[default]) são valores Rust válidos.

Por que inteiros de 64 bits são tipados como i64/u64 em vez de strings?

Em Rust não há problema de precisão — i64 e u64 são tipos de primeira classe, ao contrário do JavaScript onde valores de 64 bits viram strings. Se você está desserializando JSON produzido por um encoder JSON proto3 estrito, os valores chegarão como strings; o serde não converte automaticamente. Adicione #[serde(with = "serde_with::DisplayFromStr")] ou um desserializador customizado nesses campos se cair nesse caso.

Por que HashMap em vez de BTreeMap?

HashMap bate com o que o prost emite para campos map<K, V> e é o default certo — campos map em proto não têm ordem. Se você precisa de ordem de iteração estável, troque o tipo para BTreeMap à mão; o resto do código não precisa mudar.

Devo usar essa saída com prost ou tonic?

São structs derivados puramente de serde, não structs codificados pelo prost. Eles fazem round-trip por JSON proto3 via serde_json, mas não codificam/decodificam o formato binário do fio. Se você precisa de codificação binária protobuf, rode prost num passo build.rs. Se precisa de gRPC, use tonic. Este conversor é para o caso JSON-sobre-HTTP (gRPC-gateway, Connect, transcodificação JSON).

Lida com oneof?

Cada campo de oneof é emitido como um campo de struct comum. A saída não impõe a restrição de "exatamente um" que oneof implica — para isso você precisaria de um enum Rust com uma variante por caso, o que depende da semântica do seu runtime. Se você precisa de tratamento estrito de oneof, edite a saída à mão para um pub enum.

Ferramentas relacionadas

Se você está mexendo com Protobuf, JSON e Rust, estas combinam bem: