From 7b9a4bcff781eaa865d1cd8a982620c2f64a0808 Mon Sep 17 00:00:00 2001 From: Dragos Carp Date: Thu, 20 Dec 2018 12:22:49 +0100 Subject: [PATCH] Allow empty messages --- src/google/protobuf/common.d | 8 +++++++- src/google/protobuf/decoding.d | 11 +++++++++++ src/google/protobuf/encoding.d | 17 +++++++++++++++++ src/google/protobuf/json_decoding.d | 12 ++++++++++++ src/google/protobuf/json_encoding.d | 13 +++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/google/protobuf/common.d b/src/google/protobuf/common.d index 21b75b7..5a5c791 100644 --- a/src/google/protobuf/common.d +++ b/src/google/protobuf/common.d @@ -144,7 +144,6 @@ template Message(T) alias fieldNames = staticMap!(fieldName, fields); - static assert(fields.length > 0, "Definition of '" ~ T.stringof ~ "' has no Proto field"); static assert(allSatisfy!(validateField, fields), "'" ~ T.stringof ~ "' has invalid fields"); private alias unsortedFields = getSymbolsByUDA!(T, Proto); @@ -165,6 +164,13 @@ unittest static assert(Message!Test.fieldNames == AliasSeq!("bar", "foo")); static assert(Message!Test.protos == AliasSeq!(Proto(2, Wire.fixed, No.packed), Proto(3, Wire.none, No.packed))); + + static class EmptyMessage + { + } + + static assert(is(Message!EmptyMessage.fieldNames == AliasSeq!())); + static assert(is(Message!EmptyMessage.protos == AliasSeq!())); } template validateField(alias field) diff --git a/src/google/protobuf/decoding.d b/src/google/protobuf/decoding.d index 912f6b0..6c3fd4f 100644 --- a/src/google/protobuf/decoding.d +++ b/src/google/protobuf/decoding.d @@ -218,6 +218,17 @@ unittest assert(foo.qux); } +unittest +{ + static class EmptyMessage + { + } + + ubyte[] buff = []; + auto emptyMessage = buff.fromProtobuf!EmptyMessage; + assert(buff.empty); +} + private static void fromProtobufByField(alias field, T, R)(ref R inputRange, ref T message) if (isInputRange!R) { diff --git a/src/google/protobuf/encoding.d b/src/google/protobuf/encoding.d index 1aeb2f3..a2822c5 100644 --- a/src/google/protobuf/encoding.d +++ b/src/google/protobuf/encoding.d @@ -124,12 +124,17 @@ unittest auto toProtobuf(T)(T value) if (is(T == class) || is(T == struct)) { + import std.meta : AliasSeq; import std.traits : hasMember; static if (hasMember!(T, "toProtobuf")) { return value.toProtobuf; } + else static if (is(Message!T.fields == AliasSeq!())) + { + return cast(ubyte[]) null; + } else { import std.algorithm : joiner; @@ -188,6 +193,18 @@ unittest assert(foo.toProtobuf.array == [0x08, 0x05, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01]); } +unittest +{ + import std.array : array; + + struct EmptyMessage + { + } + + EmptyMessage emptyMessage; + assert(emptyMessage.toProtobuf.empty); +} + unittest { struct Foo diff --git a/src/google/protobuf/json_decoding.d b/src/google/protobuf/json_decoding.d index 430f679..4a334e2 100644 --- a/src/google/protobuf/json_decoding.d +++ b/src/google/protobuf/json_decoding.d @@ -294,6 +294,18 @@ unittest assertThrown!ProtobufException(fromJSONValue!Foo(parseJSON(`{"d":10, "e":"abc"}`))); } +unittest +{ + import std.json : parseJSON; + + struct EmptyMessage + { + } + + assert(fromJSONValue!EmptyMessage(parseJSON(`{}`)) == EmptyMessage()); + assert(fromJSONValue!EmptyMessage(parseJSON(`{"a":10, "b":"abc"}`)) == EmptyMessage()); +} + private template oneofs(T) { import std.meta : NoDuplicates, staticMap; diff --git a/src/google/protobuf/json_encoding.d b/src/google/protobuf/json_encoding.d index 95c7269..87967c1 100644 --- a/src/google/protobuf/json_encoding.d +++ b/src/google/protobuf/json_encoding.d @@ -137,6 +137,19 @@ unittest assert(foo.toJSONValue == parseJSON(`{"a":10, "b":"abc"}`)); } +unittest +{ + import std.json : parseJSON; + + struct EmptyMessage + { + } + + EmptyMessage emptyMessage; + + assert(emptyMessage.toJSONValue == parseJSON(`{}`)); +} + unittest { import std.json : parseJSON;