Cuando un socio te entrega un servicio gRPC, te entrega una carpeta de archivos .proto. Esos archivos son el contrato, y cada mensaje que contienen tiene que convertirse en algo que tu código Delphi pueda leer y escribir. Hacerlo a mano es tedioso y fácil de equivocar, y hay que rehacerlo cada vez que la otra parte publica una nueva versión del contrato.
sgcProtoBuf es el generador de código que lo hace por ti. Apúntalo a un archivo .proto y producirá unidades Delphi con una clase por cada mensaje, listas para serializar y deserializar. Lee proto2 y proto3, y toma la sintaxis del propio archivo. Se distribuye tanto como asistente como herramienta de línea de comandos.
Dale un archivo y obtén todo el árbol
Los árboles proto reales nunca son un solo archivo. Se importan entre sí, y además importan las definiciones de Google. Un archivo de servicio típico empieza así:
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 Resolver importaciones activado, seleccionas ese único archivo y sgcProtoBuf sigue las sentencias import de forma recursiva, generando todas las dependencias que encuentra y añadiendo las unidades generadas a la cláusula uses. La carpeta raíz de tu árbol proto se detecta automáticamente a partir del archivo que has elegido, así que normalmente no hay nada que configurar.
Si una definición no se encuentra en el disco, se informa en la lista de mensajes en lugar de producir en silencio una unidad que no compila. Ves exactamente qué archivo falta en vez de perseguir un error del compilador.
Los nombres de unidad vienen del package
La unidad generada toma el nombre de la sentencia package, de modo que package acme.telemetry.v1; produce unit acme.telemetry.v1; guardada como acme.telemetry.v1.pas. El nombre del archivo siempre coincide con el nombre de la unidad, lo que elimina toda una categoría de errores de compilación evitables.
También significa que varios archivos .proto que declaran el mismo package acaban en una sola unidad, exactamente como pretendían los autores del contrato. Una carpeta con device.proto, device_service.proto y units.proto que declaran todos acme.common.v1 se convierte en un único acme.common.v1.pas.
Los tipos de Google ya vienen incluidos
Casi todos los archivos proto reales importan algo de google.protobuf. No necesitas generar esos tipos. sgcWebSockets ya los incluye en la unidad sgcProtoBuf_WellKnownTypes, y el generador los asigna automáticamente: Timestamp, Duration, Any, Empty, FieldMask, Struct y los tipos envoltorio como StringValue e Int32Value.
Así, el campo google.protobuf.Timestamp de arriba se convierte en un TsgcProtoBufTimestamp, con ToDateTime y FromDateTime ya implementados. Los archivos .proto de Google no hay que traducirlos en absoluto.
Enumeraciones como tipos Delphi reales
Por defecto una enumeración se genera como un bloque de constantes. Activa Generar enumeraciones como tipos enumerados de Delphi y obtendrás un tipo propio en su lugar, más corto de leer e imposible de confundir con un entero sin relación:
CountryCodeDto = (
COUNTRY_CODE_DTO_NONE = 0,
COUNTRY_CODE_DTO_NL = 1,
COUNTRY_CODE_DTO_DE = 2,
COUNTRY_CODE_DTO_CH = 3
);
Hay una limitación honesta que conviene conocer. Una enumeración de Protocol Buffers puede hacer cosas que un tipo enumerado de Delphi no puede: puede contener valores negativos, puede asignar dos nombres al mismo número, y dos enumeraciones del mismo package pueden declarar el mismo nombre de valor. Cuando el generador se encuentra con uno de esos casos, recurre a la forma de constantes para esa enumeración concreta y escribe un comentario explicando por qué. Todas las demás enumeraciones del archivo no se ven afectadas, así que obtienes tipos reales allí donde es posible y una unidad funcional en todos los demás casos.
El código generado
Cada clase de mensaje desciende de una clase base generada y expone LoadFromBytes, LoadFromStream y ToBytes, con una propiedad por cada campo. El ejemplo siguiente usa la opción Mantener los nombres originales de los elementos proto, que elimina el prefijo Tsgc y conserva los nombres tal como aparecen en el 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;
En la línea de comandos
El asistente resulta cómodo para un primer vistazo, pero un árbol proto cambia, y regenerarlo forma parte de tu compilación. El mismo generador se ejecuta desde la línea de comandos:
sgcProtoBuf.exe --input=protos\acme\telemetry\v1\telemetry.proto --resolve-imports --output-dir=units --enum-as-type
Los modificadores reflejan las opciones del asistente: --resolve-imports, --import-root (repetible, para árboles repartidos entre más de una raíz), --output-dir, --enum-as-type, --original-names, --short-enum-consts, además de --no-classes, --no-services y --no-docs. Ejecútalo con --help para ver la lista completa.
Cómo conseguirlo
sgcProtoBuf se distribuye con sgcWebSockets, junto a TsgcGRPCClient y las unidades de ejecución de sgcProtoBuf. Descarga la última versión desde la página de descargas de sgcWebSockets, y consulta el tema sgcProtoBuf en la ayuda para la referencia completa de opciones.
¿Preguntas, comentarios o un árbol proto que no se genera correctamente? Ponte en contacto, recibirás respuesta de las personas que escribieron el código.
