Quando un partner ti consegna un servizio gRPC, ti consegna una cartella di file .proto. Quei file sono il contratto, e ogni messaggio che contengono deve diventare qualcosa che il tuo codice Delphi possa leggere e scrivere. Farlo a mano è noioso e facile da sbagliare, e va rifatto ogni volta che l'altra parte pubblica una nuova versione del contratto.
sgcProtoBuf è il generatore di codice che lo fa al posto tuo. Indicagli un file .proto e produce unit Delphi con una classe per ogni messaggio, pronte a serializzare e deserializzare. Legge proto2 e proto3, e ricava la sintassi dal file stesso. È disponibile sia come wizard sia come strumento a riga di comando.
Dagli un solo file, ottieni tutto l'albero
Gli alberi proto reali non sono mai un solo file. Si importano a vicenda, e in più importano le definizioni di Google. Un tipico file di servizio inizia così:
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;
}
Con Risolvi gli import attivo, selezioni quell'unico file e sgcProtoBuf segue le istruzioni import in modo ricorsivo, generando ogni dipendenza che trova e aggiungendo le unit generate alla clausola uses. La cartella radice del tuo albero proto viene individuata automaticamente a partire dal file che hai scelto, quindi di solito non c'è nulla da configurare.
Se una definizione non viene trovata su disco, la cosa viene segnalata nell'elenco dei messaggi invece di produrre in silenzio una unit che non compila. Vedi esattamente quale file manca, senza dover rincorrere un errore del compilatore.
I nomi delle unit vengono dal package
La unit generata prende il nome dall'istruzione package, quindi package acme.telemetry.v1; produce unit acme.telemetry.v1; salvata come acme.telemetry.v1.pas. Il nome del file corrisponde sempre al nome della unit, il che elimina un'intera categoria di errori di compilazione evitabili.
Significa anche che più file .proto che dichiarano lo stesso package finiscono in una sola unit, esattamente come volevano gli autori del contratto. Una cartella con device.proto, device_service.proto e units.proto che dichiarano tutti acme.common.v1 diventa un solo acme.common.v1.pas.
I tipi Google sono già inclusi
Quasi tutti i file proto reali importano qualcosa da google.protobuf. Non hai bisogno di generarli. sgcWebSockets li include già nella unit sgcProtoBuf_WellKnownTypes, e il generatore li mappa automaticamente: Timestamp, Duration, Any, Empty, FieldMask, Struct e i tipi wrapper come StringValue e Int32Value.
Così il campo google.protobuf.Timestamp qui sopra diventa un TsgcProtoBufTimestamp, con ToDateTime e FromDateTime già implementati. I file .proto di Google non devono mai essere tradotti.
Enum come veri tipi Delphi
Per impostazione predefinita un enum viene generato come un blocco di costanti. Attiva Genera gli enum come tipi enumerati Delphi e ottieni invece un vero tipo, più breve da leggere e impossibile da confondere con un intero qualsiasi:
CountryCodeDto = (
COUNTRY_CODE_DTO_NONE = 0,
COUNTRY_CODE_DTO_NL = 1,
COUNTRY_CODE_DTO_DE = 2,
COUNTRY_CODE_DTO_CH = 3
);
C'è un limite, va detto onestamente, che vale la pena conoscere. Un enum di Protocol Buffers può fare cose che un tipo enumerato Delphi non può: può contenere valori negativi, può assegnare due nomi allo stesso numero, e due enum nello stesso package possono dichiarare lo stesso nome di valore. Quando il generatore incontra uno di questi casi, per quel singolo enum ripiega sulla forma a costanti e scrive un commento che spiega il perché. Tutti gli altri enum del file non ne risentono, quindi ottieni tipi veri dove è possibile e una unit funzionante in tutti gli altri casi.
Il codice generato
Ogni classe messaggio deriva da una classe base generata ed espone LoadFromBytes, LoadFromStream e ToBytes, con una proprietà per ogni campo. L'esempio qui sotto usa l'opzione Mantieni i nomi originali degli elementi proto, che elimina il prefisso Tsgc e conserva i nomi così come compaiono nel contratto:
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;
Da riga di comando
Il wizard è comodo per una prima occhiata, ma un albero proto cambia, e rigenerarlo è un compito che appartiene alla tua build. Lo stesso generatore si esegue da riga di comando:
sgcProtoBuf.exe --input=protos\acme\telemetry\v1\telemetry.proto --resolve-imports --output-dir=units --enum-as-type
Gli switch rispecchiano le opzioni del wizard: --resolve-imports, --import-root (ripetibile, per alberi distribuiti su più di una radice), --output-dir, --enum-as-type, --original-names, --short-enum-consts, oltre a --no-classes, --no-services e --no-docs. Eseguilo con --help per l'elenco completo.
Come ottenerlo
sgcProtoBuf è incluso in sgcWebSockets, insieme a TsgcGRPCClient e alle unit runtime sgcProtoBuf. Scarica l'ultima build dalla pagina di download di sgcWebSockets, e consulta l'argomento sgcProtoBuf nella guida per il riferimento completo delle opzioni.
Domande, commenti o un albero proto che non si genera in modo pulito? Contattaci, riceverai una risposta dalle persone che hanno scritto il codice.
