Konwerter Protobuf do Rust
Wklej schemat .proto. Odbierz struktury i enumy Rust z derive’ami serde, polami w snake_case i Option<T> na nullable submessage’ach — gotowe do wrzucenia do crate’a.
Wejście (schemat .proto)
Wyjście (Rust)
Co robi to narzędzie
Masz schemat Protocol Buffers i serwis lub klienta w Rust, który musi z nim gadać. Standardowa droga to prost (albo tonic dla gRPC) wpięty w skrypt build.rs — solidne, ale przesada, gdy chcesz tylko naszkicować typy i porównać kształty z kodem. Ten konwerter zamienia schemat na definicje struktur w przeglądarce. Wklej, skopiuj, wrzuć do src/types.rs, lecisz dalej.
Wyjście używa #[derive(Debug, Default, Clone, Serialize, Deserialize)] na każdej strukturze, żeby przechodziła round-trip przez JSON dzięki serde, oraz enumów #[repr(i32)], w których wariant z wartością zerową ma #[default], więc Default::default() zgadza się z semantyką proto3. Pojedyncze zagnieżdżone wiadomości stają się Option<T> — Rust wymaga jawnej nullowalności, a to pasuje do tego, co generuje prost. Vec<T> dla pól repeated i HashMap<K, V> dla map dostają oba #[serde(default)], więc brakujące pola deserializują się do pustych kontenerów zamiast wywalać się błędem.
Mapowanie typów idzie za specyfikacją mapowania JSON proto3: string/bytes → String/Vec<u8>, różne rozmiary intów lecą na i32/i64/u32/u64, a floaty lądują na f32/f64. Nazwy pól trafiają do snake_case (to już rustowy zwyczaj, więc większość przechodzi bez zmian), a warianty enumów tracą wrzaskliwy prefiks SCREAMING_SNAKE — ORDER_STATUS_UNSPECIFIED w enumie OrderStatus robi się Unspecified, tak jak napisałbyś ręcznie. Cała konwersja idzie po stronie klienta; nic ze schematu nie opuszcza strony.
Jak tego używać
Trzy kroki. Wyjście wkleja się od ręki w dowolnym crate Rust, który ma serde w zależnościach.
Wklej swój schemat .proto
Wrzuć schemat do lewego edytora. syntax = "proto3"; u góry jest opcjonalne — parser radzi sobie z zagnieżdżonymi blokami message, deklaracjami enum, oneof, map<K, V> i opcjami pól. Importy są rozpoznawane, ale pomijane, więc jeśli zależysz od typów importowanych, wklej je inline.
Nazwy pól same trafiają do konwencji Rust: order_id zostaje order_id, shippingAddress (jeśli masz w schemacie jakiś camelCase) staje się shipping_address. Nazwy struktur i enumów zostają w PascalCase.
Przeczytaj wyjście
Po prawej: płaska lista deklaracji pub struct i pub enum. Najpierw enumy, potem wiadomości w kolejności deklaracji, z typami zagnieżdżonymi przed rodzicami. Linia use serde::{Deserialize, Serialize}; jest zawsze; use std::collections::HashMap; pojawia się tylko, gdy w schemacie są pola map.
Wrzuć do crate’a
Wklej wyjście do pliku modułu. Dopóki masz w Cargo.toml serde = { version = "1", features = ["derive"] } i serde_json, kompiluje się. Dalej to już zwykły Rust — podepnij to pod klienta reqwest, Cloudflare Workera, handler axum, gdziekolwiek potrzebujesz typowanego dostępu do danych wiadomości.
Kiedy to naprawdę oszczędza czas
Szkicowanie typów dla nowego serwisu Rust
Budujesz serwis w Rust, który konsumuje endpoint gRPC-gateway, ale jeszcze nie chcesz stawiać pełnego build.rs z prost. Wklej .proto, wrzuć struktury do types.rs, dekoduj odpowiedź jako JSON, wypuść prototyp.
Przegląd zmiany w API Protobuf
Kolega z backendu dorzucił pola do wiadomości. Chcesz zobaczyć, jak zmieni się kształt po stronie Rust bez przebudowywania całego crate’a. Wklej nowy .proto, zrób diff wyjścia z istniejącymi typami, zostaw konkretny komentarz w review.
Porównanie z kodem generowanym przez prost
Twój build używa prost z własnymi regułami type_attribute i potrzebujesz czystej referencji, jak wyglądają niezmodyfikowane typy. Wklej tu schemat, żeby porównać obok siebie. Przydatne, gdy planujesz zmiany atrybutów albo migrujesz na inny pipeline codegen.
Narzędzia CLI i jednorazowe skrypty
Piszesz małe CLI w Rust, które uderza w API JSON oparte na Protobuf. Stawianie prost dla 100-liniowego narzędzia to więcej ceremonii niż wymaga zadanie. Weź struktury stąd, derive’uj serde, wypuść binarkę.
Częste pytania
Czy mój schemat jest gdzieś wysyłany?
Nie. Parser i emitter Rust działają jako JavaScript w twojej przeglądarce. Otwórz DevTools, popatrz na zakładkę Network podczas wklejania — zero requestów. Przydatne, gdy schemat zawiera wewnętrzne nazwy typów, ścieżki pakietów albo cokolwiek, czego nie chciałbyś wysłać do zewnętrznej usługi.
Czemu zagnieżdżone wiadomości są opakowane w Option?
Rust nie ma nullowalnych referencji jak Java czy Go — albo wartość jest, albo używasz Option<T>, żeby brak był jawny. Pojedyncze submessage’y w proto3 są nullable na drucie (tag pola może w ogóle nie wystąpić), więc idiomatyczna postać w Rust to Option<Address>, a nie Address. To pasuje do tego, co generuje prost. Skalary i enumy nie są opakowywane, bo ich wartości zerowe (pusty string, 0, false, wariant #[default]) to poprawne wartości Rust.
Czemu 64-bitowe inty są typowane jako i64/u64, a nie stringi?
W Rust nie ma problemu z precyzją — i64 i u64 to typy pierwszoklasowe, inaczej niż w JavaScripcie, gdzie 64-bitowe wartości są stringami. Jeśli deserializujesz JSON z restrykcyjnego enkodera proto3 JSON, wartości przyjdą jako stringi; serde nie konwertuje ich automatycznie. Jeżeli na to trafisz, dorzuć #[serde(with = "serde_with::DisplayFromStr")] albo własny deserializer na te pola.
Czemu HashMap, a nie BTreeMap?
HashMap pasuje do tego, co prost generuje dla pól map<K, V>, i to właściwy domyślny wybór — pola map w proto są nieuporządkowane. Jeśli potrzebujesz stabilnej kolejności iteracji, ręcznie zmień typ na BTreeMap; reszta kodu nie musi się ruszyć.
Czy używać tego wyjścia z prost lub tonic?
To czyste struktury z derive serde, nie struktury enkodowane przez prost. Robią round-trip przez proto3 JSON za pomocą serde_json, ale nie enkodują ani nie dekodują binarnego formatu wire. Jeżeli potrzebujesz binarnego enkodowania protobuf, odpal prost w kroku build.rs. Jeżeli potrzebujesz gRPC, użyj tonic. Ten konwerter jest do scenariusza JSON-over-HTTP (gRPC-gateway, Connect, transkodowanie JSON).
Obsługuje oneof?
Każde pole oneof jest emitowane jako zwykłe pole struktury. Wyjście nie wymusza ograniczenia "dokładnie jeden", które niesie oneof — do tego potrzebny byłby enum w Rust z wariantem na każdy przypadek, a to zależy od semantyki twojego runtime. Jeśli potrzebujesz ścisłej obsługi oneof, ręcznie przerób wyjście na pub enum.
Powiązane narzędzia
Jeśli ogarniasz Protobuf, JSON i Rust, te narzędzia dobrze ze sobą grają: