Quand un partenaire vous confie un service gRPC, il vous confie un dossier de fichiers .proto. Ces fichiers sont le contrat, et chaque message qu'ils contiennent doit devenir quelque chose que votre code Delphi sait lire et écrire. Le faire à la main est fastidieux et propice aux erreurs, et tout est à refaire chaque fois que l'autre partie publie une nouvelle version du contrat.
sgcProtoBuf est le générateur de code qui s'en charge pour vous. Pointez-le vers un fichier .proto et il produit des unités Delphi avec une classe pour chaque message, prêtes à sérialiser et à désérialiser. Il lit proto2 et proto3, et il prend la syntaxe dans le fichier lui-même. Il est livré à la fois comme assistant et comme outil en ligne de commande.
Donnez-lui un fichier, obtenez tout l'arbre
Les véritables arbres proto ne tiennent jamais en un seul fichier. Ils s'importent les uns les autres, et ils importent par-dessus les définitions Google. Un fichier de service typique commence ainsi :
syntax = "proto3";
package acme.telemetry.v1;
import "acme/common/v1/device.proto";
import "google/protobuf/timestamp.proto";
message Reading {
string device_id = 1;
double value = 2;
google.protobuf.Timestamp taken_at = 3;
}
Avec l'option Résoudre les imports activée, vous sélectionnez ce seul fichier et sgcProtoBuf suit les instructions import de façon récursive, générant chaque dépendance qu'il rencontre et ajoutant les unités générées à la clause uses. Le dossier racine de votre arbre proto est découvert automatiquement à partir du fichier que vous avez choisi, il n'y a donc généralement rien à configurer.
Si une définition est introuvable sur le disque, elle est signalée dans la liste des messages, plutôt que de produire en silence une unité qui ne compile pas. Vous voyez exactement quel fichier manque au lieu de courir après une erreur de compilation.
Les noms d'unités viennent du package
L'unité générée porte le nom de l'instruction package, ainsi package acme.telemetry.v1; produit unit acme.telemetry.v1; enregistré sous acme.telemetry.v1.pas. Le nom du fichier correspond toujours au nom de l'unité, ce qui élimine toute une catégorie d'erreurs de compilation évitables.
Cela signifie aussi que plusieurs fichiers .proto déclarant le même package atterrissent dans une seule unité, exactement comme les auteurs du contrat l'ont voulu. Un dossier contenant device.proto, device_service.proto et units.proto déclarant tous acme.common.v1 devient un unique acme.common.v1.pas.
Les types Google sont déjà inclus
Presque tous les fichiers proto réels importent quelque chose depuis google.protobuf. Vous n'avez pas besoin de les générer. sgcWebSockets les livre déjà dans l'unité sgcProtoBuf_WellKnownTypes, et le générateur les met en correspondance automatiquement : Timestamp, Duration, Any, Empty, FieldMask, Struct et les types d'encapsulation tels que StringValue et Int32Value.
Ainsi le champ google.protobuf.Timestamp ci-dessus devient un TsgcProtoBufTimestamp, avec ToDateTime et FromDateTime déjà implémentés. Les fichiers .proto de Google n'ont jamais besoin d'être traduits.
Des enums en véritables types Delphi
Par défaut, un enum est généré sous la forme d'un bloc de constantes. Activez Générer les enums comme types énumérés Delphi et vous obtenez à la place un vrai type, plus court à lire et impossible à confondre avec un entier sans rapport :
CountryCodeDto = (
COUNTRY_CODE_DTO_NONE = 0,
COUNTRY_CODE_DTO_NL = 1,
COUNTRY_CODE_DTO_DE = 2,
COUNTRY_CODE_DTO_CH = 3
);
Il y a une limitation qu'il vaut mieux connaître, disons-le franchement. Un enum Protocol Buffers peut faire des choses qu'un type énuméré Delphi ne peut pas faire : il peut contenir des valeurs négatives, il peut associer deux noms au même nombre, et deux enums du même package peuvent déclarer le même nom de valeur. Lorsque le générateur rencontre l'un de ces cas, il retombe sur la forme constante pour cet enum précis et écrit un commentaire expliquant pourquoi. Tous les autres enums du fichier restent inchangés, vous obtenez donc de vrais types partout où c'est possible et une unité fonctionnelle partout ailleurs.
Le code généré
Chaque classe de message descend d'une classe de base générée et expose LoadFromBytes, LoadFromStream et ToBytes, avec une propriété par champ. L'exemple ci-dessous utilise l'option Conserver les noms d'origine des éléments proto, qui supprime le préfixe Tsgc et conserve les noms tels qu'ils apparaissent dans le contrat :
uses
acme.telemetry.v1;
var
oReading: Reading;
begin
oReading := Reading.Create;
Try
oReading.LoadFromBytes(vBytes);
ShowMessage(oReading.DeviceId);
ShowMessage(FloatToStr(oReading.Value));
ShowMessage(DateTimeToStr(oReading.TakenAt.ToDateTime));
vBytes := oReading.ToBytes;
Finally
oReading.Free;
End;
end;
En ligne de commande
L'assistant est pratique pour un premier aperçu, mais un arbre proto évolue, et le régénérer a sa place dans votre build. Le même générateur s'exécute en ligne de commande :
sgcProtoBuf.exe --input=protos\acme\telemetry\v1\telemetry.proto --resolve-imports --output-dir=units --enum-as-type
Les commutateurs reprennent les options de l'assistant : --resolve-imports, --import-root (répétable, pour les arbres répartis sur plusieurs racines), --output-dir, --enum-as-type, --original-names, --short-enum-consts, ainsi que --no-classes, --no-services et --no-docs. Lancez-le avec --help pour la liste complète.
Se le procurer
sgcProtoBuf est livré avec sgcWebSockets, aux côtés de TsgcGRPCClient et des unités d'exécution sgcProtoBuf. Téléchargez la dernière version depuis la page de téléchargement de sgcWebSockets, et consultez la rubrique sgcProtoBuf dans l'aide pour la référence complète des options.
Des questions, des retours ou un arbre proto qui ne se génère pas proprement ? Contactez-nous, vous recevrez une réponse des personnes qui ont écrit le code.
