Giriş (.proto şeması)

Çıkış (Rust)

Bu araç ne işe yarar

Elinde bir Protocol Buffers şeması var ve onunla konuşması gereken bir Rust servisi ya da istemcisi var. Standart yol, bir build.rs betiğine prost (gRPC için tonic) bağlamak — sağlam, ama sadece tipleri çizip kodla şekilleri eşleştirmek istediğinde abartı kalıyor. Bu dönüştürücü şemayı tarayıcının içinde struct tanımlarına çevirir. Yapıştır, kopyala, src/types.rs içine düşür, devam et.

Çıktı her struct’a #[derive(Debug, Default, Clone, Serialize, Deserialize)] koyar; böylece serde üzerinden JSON ile temiz şekilde gidip gelir. Sıfır değerli varyantı #[default] ile işaretlenmiş #[repr(i32)] enum’ları sayesinde Default::default() proto3 semantiğine uyar. Tekil iç içe message’lar Option<T> olur — Rust açık nullability ister ve bu, prost’un ürettiğiyle örtüşür. Repeated alanlar için Vec<T> ve map’ler için HashMap<K, V> her ikisi de #[serde(default)] alır; eksik alanlar hata vermek yerine boş konteynere deserialize olur.

Tip eşlemesi proto3 JSON eşleme spesifikasyonunu izler: string/bytesString/Vec<u8>, çeşitli boyutlu tamsayılar i32/i64/u32/u64’a, float’lar ise f32/f64’e iner. Alan adları snake_case’e dönüştürülür (Rust’ın zaten geleneği bu olduğundan çoğu olduğu gibi geçer); enum varyantları da bağıran SCREAMING_SNAKE öneklerini bırakır — OrderStatus enum’undaki ORDER_STATUS_UNSPECIFIED, elle yazsan ne yazardın, o oluyor: Unspecified. Dönüştürme tamamen istemci tarafında çalışır; şemandan hiçbir şey sayfayı terk etmez.

Nasıl kullanılır

Üç adım. Çıktı, bağımlılıklarında serde olan herhangi bir Rust crate’ine yapıştırmaya hazır.

1

.proto şemanı yapıştır

Şemayı sol editöre bırak. En üstteki syntax = "proto3"; isteğe bağlı — ayrıştırıcı iç içe message bloklarını, enum bildirimlerini, oneof, map<K, V> ve alan opsiyonlarını işler. Import’lar tanınır ama atlanır; bağımlı olduğun import’lı tipleri satır içinde yapıştır.

Alan adları otomatik olarak Rust geleneklerine uyar: order_id, order_id kalır; şemanda camelCase varsa shippingAddress, shipping_address olur. Struct ve enum adları PascalCase olarak kalır.

2

Çıktıyı oku

Sağ tarafta: pub struct ve pub enum bildirimlerinin düz bir listesi. Önce enum’lar, sonra bildirim sırasına göre message’lar; iç içe tipler ebeveynlerinden önce. use serde::{Deserialize, Serialize}; satırı her zaman vardır; use std::collections::HashMap; ise yalnızca şemanda map alanları varsa görünür.

3

Crate'ine düşür

Çıktıyı bir modül dosyasına yapıştır. Cargo.toml içinde serde = { version = "1", features = ["derive"] } ve serde_json olduğu sürece derlenir. Oradan sonrası sıradan Rust — bir reqwest istemcisine, bir Cloudflare Worker’a, bir axum handler’ına, message verisine tipli erişim istediğin her yere bağla.

Gerçekten zaman kazandırdığı yerler

Yeni bir Rust servisi için tipleri taslakla

Bir gRPC-gateway uç noktasını tüketen bir Rust servisi yapıyorsun, ama henüz prost ile tam bir build.rs kurmak istemiyorsun. .proto'yu yapıştır, struct'ları types.rs'ye düşür, yanıtı JSON olarak çöz, prototipi yola çıkar.

Bir Protobuf API değişikliğini gözden geçir

Bir backend takım arkadaşı bir message'a alanlar ekledi. Tüm crate'i yeniden derlemeden Rust tarafındaki şeklin nasıl değişeceğini görmek istiyorsun. Yeni .proto'yu yapıştır, çıktıyı mevcut tiplerinle diff'le, odaklı bir review yorumu bırak.

prost tarafından üretilen kodla karşılaştır

Build’in prost’u özel type_attribute kurallarıyla kullanıyor ve değiştirilmemiş tiplerin nasıl göründüğüne dair temiz bir referans istiyorsun. Şemayı buraya yapıştır, yan yana karşılaştır. Öznitelik değişikliklerini planlarken ya da farklı bir codegen pipeline’ına geçerken kullanışlı.

CLI araçları ve tek seferlik betikler

Protobuf destekli bir JSON API'sini çağıran küçük bir Rust CLI yazıyorsun. 100 satırlık bir araç için prost bağlamak, işin gerektirdiğinden daha fazla seremoni. Struct'ları buradan al, serde derive et, ikiliği yola çıkar.

Sık sorulan sorular

Şemam herhangi bir yere gönderiliyor mu?

Hayır. Ayrıştırıcı da Rust üreteci de tarayıcında JavaScript olarak çalışıyor. DevTools’u aç, yapıştırırken Network sekmesini izle — sıfır istek. Şeman dahili tip adları, paket yolları veya üçüncü taraf bir servise göndermeyeceğin şeyler içeriyorsa kullanışlı.

İç içe message’lar neden Option içinde sarılı?

Rust’ta Java veya Go’daki gibi nullable referanslar yok — bir değer ya mevcuttur ya da yokluğunu açık etmek için Option<T> kullanırsın. proto3’te tekil submessage’lar tel üzerinde nullable’dır (alan etiketi tamamen eksik olabilir), dolayısıyla deyimsel Rust formu Address değil Option<Address>’tir. Bu, prost’un ürettiği biçimle örtüşür. Skalerler ve enum’lar sarılmaz çünkü sıfır değerleri (boş dize, 0, false, #[default] enum varyantı) geçerli Rust değerleridir.

64 bit tamsayılar neden string yerine i64/u64 olarak tipleniyor?

Rust’ta hassasiyet endişesi yok — i64 ve u64 birinci sınıf tipler; 64 bit değerlerin string olduğu JavaScript’ten farklı. Sıkı bir proto3 JSON encoder’ından gelen JSON’u deserialize ediyorsan, değerler string olarak gelir; serde otomatik dönüştürmez. Böyle bir durumda ilgili alanlara #[serde(with = "serde_with::DisplayFromStr")] ya da özel bir deserializer ekle.

Neden BTreeMap yerine HashMap?

HashMap, prost’un map<K, V> alanları için ürettiğiyle örtüşür ve doğru varsayılandır — proto’nun map alanları sırasızdır. Sabit bir iterasyon sırası gerekiyorsa, tipi elle BTreeMap’e çevir; kodun geri kalanı değişmek zorunda değil.

Bu çıktıyı prost veya tonic ile mi kullanmalıyım?

Bunlar düz serde-derive struct’ları, prost ile encode edilmiş struct’lar değil. serde_json üzerinden proto3 JSON ile gidip gelirler ama ikili tel formatını encode/decode etmezler. İkili protobuf encoding gerekiyorsa prost’u bir build.rs adımıyla çalıştır. gRPC gerekiyorsa tonic kullan. Bu dönüştürücü JSON-over-HTTP senaryosu içindir (gRPC-gateway, Connect, JSON transcoding).

oneof’u destekliyor mu?

Bir oneof’un her alanı normal bir struct alanı olarak çıkar. Çıktı, oneof’un ima ettiği "tam olarak bir tane" kısıtını uygulamaz — bunun için her durum için bir varyant içeren bir Rust enum’una ihtiyaç olur ki bu da çalışma zamanı semantiğine bağlıdır. Sıkı bir oneof işleme gerekiyorsa, çıktıyı elle bir pub enum’a dönüştür.

İlgili araçlar

Protobuf, JSON ve Rust ile uğraşıyorsan şunlar iyi gider: