Entrée (schéma .proto)

Sortie (Rust)

Ce que fait cet outil

Tu as un schéma Protocol Buffers et un service ou client Rust qui doit lui parler. Le chemin standard, c’est prost (ou tonic pour gRPC) câblé dans un script build.rs — solide, mais surdimensionné quand tu veux juste esquisser les types et vérifier les formes contre ton code. Ce convertisseur transforme le schéma en définitions de struct dans ton navigateur. Colle, copie, dépose dans src/types.rs, avance.

La sortie utilise #[derive(Debug, Default, Clone, Serialize, Deserialize)] sur chaque struct pour qu’elle fasse l’aller-retour via JSON avec serde, et des enums #[repr(i32)] dont la variante de valeur zéro porte #[default], si bien que Default::default() respecte la sémantique de proto3. Les messages imbriqués singuliers deviennent Option<T> — Rust impose une nullabilité explicite, et ça correspond à ce que prost génère. Vec<T> pour les champs repeated et HashMap<K, V> pour les maps reçoivent tous deux #[serde(default)] pour que les champs absents désérialisent vers des conteneurs vides au lieu de planter.

Le mapping de types suit la spec de mapping JSON proto3 : string/bytesString/Vec<u8>, les différents entiers dimensionnés se mappent vers i32/i64/u32/u64, et les flottants atterrissent en f32/f64. Les noms de champ sont convertis en snake_case (déjà la convention Rust, donc la plupart passent inchangés), et les variantes d’enum perdent le préfixe d’enum en SCREAMING_SNAKE — ORDER_STATUS_UNSPECIFIED dans l’enum OrderStatus devient Unspecified, comme tu l’écrirais à la main. La conversion tourne entièrement côté client ; rien de ton schéma ne quitte la page.

Comment l’utiliser

Trois étapes. La sortie est prête à coller dans n’importe quel crate Rust qui a serde dans ses dépendances.

1

Colle ton schéma .proto

Dépose le schéma dans l’éditeur de gauche. syntax = "proto3"; en tête est optionnel — le parser gère les blocs message imbriqués, les déclarations enum, oneof, map<K, V> et les options de champ. Les imports sont reconnus puis ignorés, donc colle les types importés inline si tu en dépends.

Les noms de champ suivent automatiquement les conventions Rust : order_id reste order_id, shippingAddress (si tu as du camelCase dans ton schéma) devient shipping_address. Les noms de struct et d’enum restent en PascalCase.

2

Lis la sortie

À droite : une liste plate de déclarations pub struct et pub enum. Les enums viennent en premier, puis les messages dans l’ordre de déclaration, avec les types imbriqués devant leurs parents. La ligne use serde::{Deserialize, Serialize}; est toujours là ; use std::collections::HashMap; n’apparaît que si ton schéma contient des champs map.

3

Dépose dans ton crate

Colle la sortie dans un fichier de module. Tant que tu as serde = { version = "1", features = ["derive"] } et serde_json dans ton Cargo.toml, ça compile. À partir de là, c’est du Rust normal — branche-le sur un client reqwest, un Cloudflare Worker, un handler axum, partout où tu veux un accès typé aux données du message.

Quand ça fait vraiment gagner du temps

Esquisser les types pour un nouveau service Rust

Tu construis un service Rust qui consomme un endpoint gRPC-gateway, mais tu n’as pas encore envie de monter un build.rs complet avec prost. Colle le .proto, dépose les structs dans types.rs, décode la réponse JSON, livre le prototype.

Relire un changement d’API Protobuf

Un coéquipier backend a ajouté des champs à un message. Tu veux voir comment la forme côté Rust va changer sans recompiler tout le crate. Colle le nouveau .proto, diff la sortie contre tes types existants, laisse une remarque de revue ciblée.

Recouper le code généré par prost

Ton build utilise prost avec des règles type_attribute custom et tu veux une référence propre de l’apparence des types non modifiés. Colle le schéma ici pour comparer côte à côte. Utile quand tu prévois des changements d’attributs ou tu migres vers un autre pipeline de codegen.

Outils CLI et scripts ponctuels

Tu écris un petit CLI Rust qui appelle une API JSON adossée à Protobuf. Câbler prost juste pour un outil de 100 lignes, c’est plus de cérémonie que ce que demande le job. Récupère les structs ici, dérive serde, livre le binaire.

Questions fréquentes

Mon schéma part-il quelque part ?

Non. Le parser et l’émetteur Rust tournent tous les deux en JavaScript dans ton navigateur. Ouvre les DevTools, regarde l’onglet Network pendant que tu colles — zéro requête. Pratique quand ton schéma contient des noms de types internes, des chemins de package, ou tout ce que tu n’enverrais pas à un service tiers.

Pourquoi les messages imbriqués sont-ils enveloppés dans Option ?

Rust n’a pas de références nullable comme Java ou Go — soit la valeur est présente, soit tu utilises Option<T> pour rendre l’absence explicite. Les sous-messages singuliers en proto3 sont nullable sur le fil (le tag de champ peut être absent en entier), donc la forme idiomatique en Rust est Option<Address>, pas Address. Ça correspond à ce que prost génère. Les scalaires et enums ne sont pas enveloppés parce que leurs valeurs zéro (chaîne vide, 0, false, la variante d’enum #[default]) sont des valeurs Rust valides.

Pourquoi les entiers 64 bits sont-ils typés i64/u64 plutôt que strings ?

En Rust, pas de souci de précision — i64 et u64 sont des types first-class, contrairement à JavaScript où les valeurs 64 bits sont des strings. Si tu désérialises du JSON produit par un encoder JSON proto3 strict, les valeurs arriveront en strings ; serde ne convertit pas automatiquement. Ajoute #[serde(with = "serde_with::DisplayFromStr")] ou un désérialiseur custom sur ces champs si tu tombes sur ce cas.

Pourquoi HashMap plutôt que BTreeMap ?

HashMap correspond à ce que prost émet pour les champs map<K, V> et c’est le bon défaut — les champs map de proto sont non ordonnés. Si tu as besoin d’un ordre d’itération stable, change le type pour BTreeMap à la main ; le reste du code n’a pas besoin de bouger.

Dois-je utiliser cette sortie avec prost ou tonic ?

Ce sont de simples structs dérivés serde, pas des structs encodés prost. Ils font l’aller-retour via JSON proto3 avec serde_json, mais ils n’encodent/décodent pas le format binaire wire. Si tu as besoin d’encodage binaire protobuf, fais tourner prost dans une étape build.rs. Si tu as besoin de gRPC, utilise tonic. Ce convertisseur est pour le cas JSON-sur-HTTP (gRPC-gateway, Connect, transcodage JSON).

Gère-t-il oneof ?

Chaque champ d’un oneof est émis comme un champ de struct ordinaire. La sortie n’impose pas la contrainte « exactement un » qu’implique oneof — pour ça il faudrait un enum Rust avec une variante par cas, ce qui dépend de la sémantique de ton runtime. Si tu as besoin d’un traitement strict de oneof, édite la sortie à la main en un pub enum.

Outils associés

Si tu manipules Protobuf, JSON et Rust, ces outils vont bien ensemble :