Eingabe (.proto-Schema)

Ausgabe (Rust)

Was dieses Tool macht

Du hast ein Protocol-Buffers-Schema und einen Rust-Service oder -Client, der damit reden muss. Der Standardweg ist prost (oder tonic für gRPC), eingebunden in ein build.rs-Skript — solide, aber überdimensioniert, wenn du nur die Typen skizzieren und Formen gegen deinen Code prüfen willst. Dieser Konverter macht aus dem Schema Struct-Definitionen direkt im Browser. Einfügen, kopieren, in src/types.rs ablegen, weitermachen.

Die Ausgabe nutzt #[derive(Debug, Default, Clone, Serialize, Deserialize)] auf jedem Struct, sodass er via serde sauber durch JSON läuft, und #[repr(i32)]-Enums mit der Zero-Value-Variante als #[default] markiert, damit Default::default() der proto3-Semantik entspricht. Singuläre verschachtelte Messages werden zu Option<T> — Rust verlangt explizite Nullability, und das passt zu dem, was prost generiert. Vec<T> für repeated-Felder und HashMap<K, V> für Maps bekommen beide #[serde(default)], sodass fehlende Felder zu leeren Containern deserialisieren statt zu fehlschlagen.

Das Type-Mapping folgt der proto3-JSON-Mapping-Spec: string/bytesString/Vec<u8>, die verschiedenen dimensionierten Integer mappen auf i32/i64/u32/u64, und die Floats landen auf f32/f64. Feldnamen werden zu snake_case konvertiert (ist ohnehin Rust-Konvention, also gehen die meisten unverändert durch), und Enum-Varianten verlieren das schreiende SCREAMING_SNAKE-Präfix — ORDER_STATUS_UNSPECIFIED im Enum OrderStatus wird zu Unspecified, so wie du es per Hand schreiben würdest. Die Konvertierung läuft komplett clientseitig; nichts von deinem Schema verlässt die Seite.

So benutzt du es

Drei Schritte. Die Ausgabe ist sofort einfügbar in jedes Rust-Crate, das serde in den Dependencies hat.

1

Füge dein .proto-Schema ein

Lass das Schema in den linken Editor fallen. syntax = "proto3"; oben ist optional — der Parser kommt mit verschachtelten message-Blöcken, enum-Deklarationen, oneof, map<K, V> und Feldoptionen klar. Imports werden erkannt, aber übersprungen — füge importierte Typen also inline ein, wenn du auf sie angewiesen bist.

Feldnamen folgen automatisch den Rust-Konventionen: order_id bleibt order_id, shippingAddress (falls du irgendwo camelCase im Schema hast) wird zu shipping_address. Struct- und Enum-Namen bleiben PascalCase.

2

Lies die Ausgabe

Rechts: eine flache Liste von pub struct- und pub enum-Deklarationen. Enums kommen zuerst, dann Messages in Deklarationsreihenfolge, mit verschachtelten Typen vor ihren Eltern. Die Zeile use serde::{Deserialize, Serialize}; ist immer da; use std::collections::HashMap; taucht nur auf, wenn dein Schema Map-Felder hat.

3

Ab in dein Crate

Füge die Ausgabe in eine Modul-Datei ein. Solange du serde = { version = "1", features = ["derive"] } und serde_json in deiner Cargo.toml hast, kompiliert es. Ab da ist es ganz normales Rust — häng es an einen reqwest-Client, einen Cloudflare Worker, einen axum-Handler, überall dort, wo du typisierten Zugriff auf die Message-Daten brauchst.

Wann das wirklich Zeit spart

Typen für einen neuen Rust-Service skizzieren

Du baust einen Rust-Service, der einen gRPC-gateway-Endpunkt anspricht, willst aber noch kein vollständiges build.rs mit prost aufsetzen. Füge das .proto ein, lass die Structs in types.rs fallen, dekodiere die Antwort als JSON, schick den Prototyp raus.

Eine Protobuf-API-Änderung reviewen

Ein Backend-Kollege hat einer Message Felder hinzugefügt. Du willst sehen, wie sich die Form auf der Rust-Seite ändert, ohne das ganze Crate neu zu bauen. Füge das neue .proto ein, vergleiche die Ausgabe mit deinen bestehenden Typen, hinterlasse einen fokussierten Review-Kommentar.

prost-generierten Code abgleichen

Dein Build nutzt prost mit eigenen type_attribute-Regeln und du willst eine saubere Referenz, wie die unmodifizierten Typen aussehen. Füge das Schema hier ein für einen Vergleich nebeneinander. Hilfreich, wenn du Attributänderungen planst oder zu einer anderen Codegen-Pipeline migrierst.

CLI-Tools und Wegwerf-Skripte

Du schreibst eine kleine Rust-CLI, die eine JSON-API anruft, die hinten Protobuf trägt. prost nur für ein 100-Zeilen-Tool zu verkabeln, ist mehr Zeremonie, als der Job verlangt. Hol dir die Structs hier, derive serde, schick das Binary raus.

Häufige Fragen

Wird mein Schema irgendwohin geschickt?

Nein. Parser und Rust-Emitter laufen beide als JavaScript in deinem Browser. Öffne die DevTools, beobachte den Network-Tab beim Einfügen — null Requests. Praktisch, wenn dein Schema interne Typnamen, Paketpfade oder irgendwas enthält, das du nicht an einen Drittanbieter schicken würdest.

Warum sind verschachtelte Messages in Option verpackt?

Rust hat keine nullbaren Referenzen wie Java oder Go — entweder ist ein Wert da, oder du nutzt Option<T>, um die Abwesenheit explizit zu machen. Singuläre Submessages in proto3 sind auf der Leitung nullbar (der Field-Tag kann komplett fehlen), also ist die idiomatische Rust-Form Option<Address>, nicht Address. Das passt zu dem, was prost generiert. Skalare und Enums werden nicht verpackt, weil ihre Zero-Werte (leerer String, 0, false, die #[default]-Enum-Variante) gültige Rust-Werte sind.

Warum sind 64-Bit-Integer als i64/u64 typisiert statt als String?

In Rust gibt es kein Präzisionsproblem — i64 und u64 sind First-Class-Typen, anders als in JavaScript, wo 64-Bit-Werte zu Strings werden. Wenn du JSON aus einem strikten proto3-JSON-Encoder deserialisierst, kommen die Werte als Strings an; serde konvertiert nicht automatisch. Pack #[serde(with = "serde_with::DisplayFromStr")] oder einen eigenen Deserializer auf diese Felder, wenn dir das passiert.

Warum HashMap statt BTreeMap?

HashMap entspricht dem, was prost für map<K, V>-Felder ausgibt, und ist der richtige Default — proto-Map-Felder sind ungeordnet. Wenn du eine stabile Iterationsreihenfolge brauchst, ändere den Typ von Hand auf BTreeMap; der Rest des Codes muss sich nicht ändern.

Soll ich diese Ausgabe mit prost oder tonic verwenden?

Das sind reine serde-derivierte Structs, keine prost-encodierten. Sie machen Round-Trip durch proto3-JSON via serde_json, kodieren/dekodieren aber kein binäres Wire-Format. Wenn du binäres Protobuf-Encoding brauchst, lass prost in einem build.rs-Schritt laufen. Brauchst du gRPC, nimm tonic. Dieser Konverter ist für den JSON-über-HTTP-Fall (gRPC-gateway, Connect, JSON-Transcoding).

Wird oneof unterstützt?

Jedes oneof-Feld wird als ganz normales Struct-Feld ausgegeben. Die Ausgabe erzwingt nicht die "Genau eins"-Bedingung, die oneof impliziert — dafür bräuchtest du ein Rust-Enum mit einer Variante pro Fall, was von deiner Runtime-Semantik abhängt. Wenn du striktes oneof-Handling brauchst, baue die Ausgabe von Hand zu einem pub enum um.

Verwandte Tools

Wenn du mit Protobuf, JSON und Rust hantierst, passen diese gut zusammen: