Convertitore da Protobuf a Rust
Incolla uno schema .proto. Ottieni struct ed enum Rust con derive di serde, campi in snake_case e Option<T> sui submessage nullable — pronti da incollare nel tuo crate.
Input (schema .proto)
Output (Rust)
Cosa fa questo tool
Hai uno schema Protocol Buffers e un servizio o client Rust che deve parlarci. La via standard è prost (o tonic per gRPC) collegato a uno script build.rs — solido, ma sproporzionato quando vuoi solo abbozzare i tipi e confrontare le forme con il tuo codice. Questo convertitore trasforma lo schema in definizioni di struct dentro al browser. Incolla, copia, fai cadere in src/types.rs, vai avanti.
L’output usa #[derive(Debug, Default, Clone, Serialize, Deserialize)] su ogni struct così che faccia round-trip in JSON via serde, ed enum #[repr(i32)] con la variante a valore zero contrassegnata #[default] in modo che Default::default() rispecchi la semantica di proto3. I message annidati singolari diventano Option<T> — Rust impone una nullabilità esplicita, e questo combacia con quello che prost genera. Vec<T> per i campi repeated e HashMap<K, V> per le map ricevono entrambi #[serde(default)], così i campi mancanti deserializzano in container vuoti invece di andare in errore.
La mappatura dei tipi segue la specifica di mapping JSON di proto3: string/bytes → String/Vec<u8>, i vari interi dimensionati mappano su i32/i64/u32/u64, e i float atterrano su f32/f64. I nomi dei campi vengono convertiti in snake_case (è già la convenzione Rust, quindi la maggior parte passa intatta), e le varianti di enum perdono il prefisso urlato in SCREAMING_SNAKE — ORDER_STATUS_UNSPECIFIED nell’enum OrderStatus diventa Unspecified, esattamente come la scriveresti a mano. La conversione gira interamente lato client; nulla del tuo schema lascia la pagina.
Come si usa
Tre passi. L’output è pronto da incollare in qualsiasi crate Rust che abbia serde tra le dipendenze.
Incolla il tuo schema .proto
Lascia cadere lo schema nell’editor di sinistra. syntax = "proto3"; in cima è opzionale — il parser gestisce blocchi message annidati, dichiarazioni enum, oneof, map<K, V> e opzioni di campo. Gli import vengono riconosciuti ma saltati, quindi se dipendi da tipi importati incollali inline.
I nomi dei campi seguono automaticamente le convenzioni Rust: order_id resta order_id, shippingAddress (se hai del camelCase nello schema) diventa shipping_address. I nomi di struct ed enum restano in PascalCase.
Leggi l’output
A destra: una lista piatta di dichiarazioni pub struct e pub enum. Gli enum vengono prima, poi i message in ordine di dichiarazione, con i tipi annidati prima dei genitori. La riga use serde::{Deserialize, Serialize}; c’è sempre; use std::collections::HashMap; compare solo se il tuo schema ha campi map.
Incolla nel tuo crate
Incolla l’output in un file di modulo. Finché hai serde = { version = "1", features = ["derive"] } e serde_json nel tuo Cargo.toml, compila. Da lì in poi è normale Rust — collegalo a un client reqwest, a un Cloudflare Worker, a un handler axum, ovunque ti serva accesso tipato ai dati del messaggio.
Quando ti fa risparmiare davvero tempo
Abbozzare i tipi per un nuovo servizio Rust
Stai costruendo un servizio Rust che consuma un endpoint gRPC-gateway, ma non vuoi ancora montare un build.rs completo con prost. Incolla il .proto, butta gli struct in types.rs, decodifica la risposta in JSON, manda fuori il prototipo.
Revisionare un cambiamento di API Protobuf
Un collega del backend ha aggiunto campi a un message. Vuoi vedere come cambia la forma lato Rust senza ricompilare l’intero crate. Incolla il nuovo .proto, fai diff dell’output con i tuoi tipi attuali, lascia un commento di review mirato.
Confronto col codice generato da prost
La tua build usa prost con regole type_attribute personalizzate e vuoi un riferimento pulito di come si vedono i tipi non modificati. Incolla qui lo schema per un confronto fianco a fianco. Utile quando pianifichi cambi di attributi o migri verso un’altra pipeline di codegen.
CLI e script una tantum
Stai scrivendo una piccola CLI in Rust che chiama un’API JSON dietro Protobuf. Cablare prost solo per uno strumento da 100 righe è più cerimonia di quella che il lavoro chiede. Prendi gli struct da qui, derive serde, manda fuori il binario.
Domande frequenti
Il mio schema viene mandato da qualche parte?
No. Il parser e l’emettitore Rust girano entrambi come JavaScript nel tuo browser. Apri i DevTools, guarda la tab Network mentre incolli — zero richieste. Utile quando lo schema include nomi di tipi interni, path di pacchetto o qualsiasi cosa che non manderesti a un servizio di terzi.
Perché i message annidati sono incartati in Option?
Rust non ha riferimenti nullable come Java o Go — o il valore è presente, oppure usi Option<T> per rendere esplicita l’assenza. I submessage singolari in proto3 sono nullable sul filo (il tag del campo può mancare interamente), quindi la forma idiomatica in Rust è Option<Address>, non Address. Combacia con quello che prost genera. Scalari ed enum non vengono incartati perché i loro valori zero (stringa vuota, 0, false, la variante #[default]) sono valori Rust validi.
Perché gli interi a 64 bit sono tipizzati come i64/u64 invece di stringhe?
In Rust non c’è un problema di precisione — i64 e u64 sono tipi di prima classe, a differenza di JavaScript dove i valori a 64 bit sono stringhe. Se stai deserializzando JSON prodotto da un encoder JSON proto3 stretto, i valori arriveranno come stringhe; serde non converte in automatico. Aggiungi #[serde(with = "serde_with::DisplayFromStr")] o un deserializer custom su quei campi se ti capita.
Perché HashMap invece di BTreeMap?
HashMap combacia con quello che prost emette per i campi map<K, V> ed è il default giusto — i campi map di proto non hanno ordine. Se ti serve un ordine di iterazione stabile, cambia il tipo a BTreeMap a mano; il resto del codice non deve cambiare.
Devo usare questo output con prost o tonic?
Sono semplici struct derivati con serde, non struct codificati da prost. Fanno round-trip via JSON proto3 con serde_json, ma non codificano/decodificano il formato binario sul filo. Se ti serve la codifica binaria di protobuf, esegui prost in uno step build.rs. Se ti serve gRPC, usa tonic. Questo convertitore è per il caso JSON-su-HTTP (gRPC-gateway, Connect, transcodifica JSON).
Gestisce oneof?
Ogni campo di un oneof viene emesso come un campo struct normale. L’output non impone il vincolo "esattamente uno" che oneof implica — per quello servirebbe un enum Rust con una variante per ogni caso, e dipende dalla semantica del tuo runtime. Se ti serve una gestione stretta di oneof, edita l’output a mano in un pub enum.
Strumenti correlati
Se stai armeggiando con Protobuf, JSON e Rust, questi vanno bene insieme: