Quando um parceiro lhe entrega um serviço gRPC, ele lhe entrega uma pasta de arquivos .proto. Esses arquivos são o contrato, e cada mensagem contida neles precisa virar algo que o seu código Delphi consiga ler e escrever. Fazer isso à mão é tedioso e fácil de errar, e tudo precisa ser refeito toda vez que o outro lado publica uma nova versão do contrato.
O sgcProtoBuf é o gerador de código que faz isso por você. Aponte para um arquivo .proto e ele produz units Delphi com uma classe para cada mensagem, prontas para serializar e desserializar. Ele lê proto2 e proto3, e obtém a sintaxe a partir do próprio arquivo. É distribuído tanto como assistente quanto como ferramenta de linha de comando.
Informe um arquivo, receba a árvore inteira
Árvores proto reais nunca são um único arquivo. Elas se importam entre si, e ainda importam as definições do Google por cima disso. Um arquivo de serviço típico começa assim:
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;
}
Com a opção Resolver imports ativada, você seleciona aquele único arquivo e o sgcProtoBuf segue as instruções import recursivamente, gerando cada dependência que encontra e adicionando as units geradas à cláusula uses. A pasta raiz da sua árvore proto é descoberta automaticamente a partir do arquivo que você escolheu, então normalmente não há nada a configurar.
Se uma definição não for encontrada em disco, isso é informado na lista de mensagens, em vez de gerar silenciosamente uma unit que não compila. Você vê exatamente qual arquivo está faltando, sem precisar caçar um erro de compilação.
Os nomes das units vêm do package
A unit gerada recebe o nome da instrução package, então package acme.telemetry.v1; produz unit acme.telemetry.v1; salva como acme.telemetry.v1.pas. O nome do arquivo sempre coincide com o nome da unit, o que elimina toda uma categoria de erros de compilação evitáveis.
Isso também significa que vários arquivos .proto que declaram o mesmo package acabam em uma única unit, exatamente como os autores do contrato pretendiam. Uma pasta com device.proto, device_service.proto e units.proto, todos declarando acme.common.v1, se torna um único acme.common.v1.pas.
Os tipos do Google já vêm na caixa
Quase todo arquivo proto real importa algo de google.protobuf. Você não precisa gerar esses tipos. O sgcWebSockets já os distribui na unit sgcProtoBuf_WellKnownTypes, e o gerador os mapeia automaticamente: Timestamp, Duration, Any, Empty, FieldMask, Struct e os tipos wrapper, como StringValue e Int32Value.
Assim, o campo google.protobuf.Timestamp acima se torna um TsgcProtoBufTimestamp, com ToDateTime e FromDateTime já implementados. Os arquivos .proto do Google nunca precisam ser traduzidos.
Enums como tipos Delphi de verdade
Por padrão, um enum é gerado como um bloco de constantes. Ative Gerar enums como tipos enumerados Delphi e você obtém um tipo de verdade no lugar, que é mais curto de ler e não pode ser confundido com um inteiro qualquer:
CountryCodeDto = (
COUNTRY_CODE_DTO_NONE = 0,
COUNTRY_CODE_DTO_NL = 1,
COUNTRY_CODE_DTO_DE = 2,
COUNTRY_CODE_DTO_CH = 3
);
Há uma limitação honesta que vale conhecer. Um enum do Protocol Buffers pode fazer coisas que um tipo enumerado Delphi não pode: ele pode conter valores negativos, pode ter dois nomes como alias para o mesmo número, e dois enums no mesmo package podem declarar o mesmo nome de valor. Quando o gerador encontra um desses casos, ele volta à forma de constantes para aquele enum específico e escreve um comentário explicando o motivo. Todos os demais enums do arquivo não são afetados, então você tem tipos reais sempre que possível e uma unit funcional em todos os outros casos.
O código gerado
Cada classe de mensagem descende de uma classe base gerada e expõe LoadFromBytes, LoadFromStream e ToBytes, com uma propriedade para cada campo. O exemplo abaixo usa a opção Manter os nomes originais dos elementos proto, que remove o prefixo Tsgc e mantém os nomes como aparecem no contrato:
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;
Na linha de comando
O assistente é conveniente para uma primeira olhada, mas uma árvore proto muda, e regenerá-la faz parte do seu build. O mesmo gerador roda a partir da linha de comando:
sgcProtoBuf.exe --input=protos\acme\telemetry\v1\telemetry.proto --resolve-imports --output-dir=units --enum-as-type
Os switches espelham as opções do assistente: --resolve-imports, --import-root (repetível, para árvores espalhadas por mais de uma raiz), --output-dir, --enum-as-type, --original-names, --short-enum-consts, além de --no-classes, --no-services e --no-docs. Execute com --help para ver a lista completa.
Como obter
O sgcProtoBuf acompanha o sgcWebSockets, junto com o TsgcGRPCClient e as units de runtime do sgcProtoBuf. Baixe a versão mais recente na página de download do sgcWebSockets, e consulte o tópico sgcProtoBuf na ajuda para a referência completa das opções.
Dúvidas, comentários ou uma árvore proto que não gera corretamente? Entre em contato, você receberá uma resposta das pessoas que escreveram o código.
