msoucy / dproto

D Protocol Buffer mixins to create structures at compile time
Boost Software License 1.0
37 stars 16 forks source link
d dub protocol-buffers

D Protocol Buffers


Build Status Coverage Status DUB DUB license

Protocol buffers are a language-agnostic way of specifying message structures to allow communication and serialization.

dproto is designed to enable mixing protocol buffer files into your D code at compile time.

Inspiration and a good portion of the original parser is adapted from square/protoparser


Options

dproto supports altering behavior via protobuf options:

Option Meaning Example Default
dproto_reserved_fmt The format for renaming reserved D keywords as fields. "%s_" will convert version to version_ "%s_"

Examples

Further info

Examples can be found in import/dproto/dproto.d and in examples/.

Simple Example

import std.stdio;
import dproto.dproto;

mixin ProtocolBufferFromString!"
    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phone = 4;
    }
";

int main()
{
    Person person;
    person.name = "John Doe";
    person.id = 1234;
    person.email = "jdoe@example.com";

    ubyte[] serializedObject = person.serialize();

    Person person2 = Person(serializedObject);
    writeln("Name: ", person2.name);
    writeln("E-mail: ", person2.email);
    return 0;
}

More Complex Example

import dproto.dproto;

mixin ProtocolBufferFromString!"
    enum PhoneType {
      MOBILE = 0;
      HOME = 0;
      WORK = 2;
    }

    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phone = 4;
    }

    message AddressBook {
      repeated Person person = 1;
    }
";

int main()
{
    Person t;
    t.name = "Max Musterman";
    t.id = 3;
    t.email = "test@example.com";

    Person.PhoneNumber pn1;
    pn1.number = "0123456789";
    pn1.type = PhoneType.WORK;

    Person.PhoneNumber pn2;
    pn2.number = "0123456789";

    t.phone = [pn1, pn2];
    AddressBook addressbook;
    addressbook.person ~= t;
    addressbook.person ~= t;

    ubyte[] serializedObject = addressbook.serialize();

    AddressBook addressbook2 = AddressBook(serializedObject);
    assert(addressbook2.person.length == 2);
    foreach(t2; addressbook2.person)
    {
        assert(t2.name == "Max Musterman");
        assert(t2.id == 3);
        assert(t2.email == "test@example.com");
        assert(t2.email !is null);
        assert(t2.phone[0].number == "0123456789");
        assert(t2.phone[0].type == PhoneType.WORK);
        assert(t2.phone[1].number == "0123456789");
        assert(t2.phone[1].type == PhoneType.HOME);
        assert(t2.phone[1].type == PhoneType.MOBILE);
        assert(t2.phone.length == 2);
    }
    version(DigitalMars)
    {
        assert(addressbook2.person[0] == addressbook.person[1]);
    }
    return 0;
}

Services

Generate interfaces for service definitions.

import dproto.dproto;

mixin ProtocolBufferInterface!"
    message ServiceRequest {
        string request = 1;
    }
    message ServiceResponse {
        string response = 1;
    }
    service TestService {
        rpc TestMethod (ServiceRequest) returns (ServiceResponse);
    }
";

class ServiceImplementation : TestService {
    ServiceResponse TestMethod(ServiceRequest input) {
        ServiceResponse output;
        output.response = "received: " ~ input.request;
        return output;
    }
}