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/bytesString/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.

1

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.

2

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.

3

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ą: