Skip to content

Commit

Permalink
proto3 の optional に対応する
Browse files Browse the repository at this point in the history
  • Loading branch information
melpon committed Jan 19, 2024
1 parent 65d4426 commit 75ef89d
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 20 deletions.
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@

## develop

## 0.11.0 (2024-01-19)

- [CHANGE] oneof の enum 値を `<FIELD_NAME>_CASE_NOT_SET` から `NOT_SET` に変更
- @melpon
- [ADD] proto3 の optional 仕様に対応
- @melpon
- [FIX] 1つのメッセージの中に oneof を2つ以上定義するとコンパイルエラーになるのを修正
- @melpon

## 0.10.0 (2023-11-14)

- [CHANGE] enum class にするのをやめて enum で定義する
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ proto ファイルから、JSON フォーマットでやりとりする型定義
- [x] message, enum 対応
- [x] repeated 対応
- [x] oneof 対応
- [x] optional 対応
- [x] bytes 型の対応( protoc-gen-json-cpp のみ)
- [x] オブジェクトの等値判定対応
- [x] テスト
Expand Down
2 changes: 2 additions & 0 deletions cmd/protoc-gen-jsonif-c/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/melpon/protoc-gen-jsonif/cmd/internal"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/pluginpb"
)
Expand Down Expand Up @@ -958,6 +959,7 @@ func genFile(file *descriptorpb.FileDescriptorProto, files []*descriptorpb.FileD

func gen(req *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse, error) {
resp := &pluginpb.CodeGeneratorResponse{}
resp.SupportedFeatures = proto.Uint64(uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL))
for _, file := range req.ProtoFile {
respFiles, err := genFile(file, req.ProtoFile)
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions cmd/protoc-gen-jsonif-cpp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,15 @@ func genEnum(enum *descriptorpb.EnumDescriptorProto, pkg *string, parents []*des
func genOneof(oneof *descriptorpb.OneofDescriptorProto, fields []*descriptorpb.FieldDescriptorProto, pkg *string, parents []*descriptorpb.DescriptorProto, cpp *cppFile) error {
typeName := internal.ToUpperCamel(*oneof.Name) + "Case"
fieldName := internal.ToSnakeCase(*oneof.Name) + "_case"
upperName := strings.ToUpper(internal.ToSnakeCase(*oneof.Name))
cpp.Typedefs.PI("enum class %s {", typeName)
cpp.Typedefs.P("%s_NOT_SET = 0,", upperName)
cpp.Typedefs.P("NOT_SET = 0,")
for _, field := range fields {
cpp.Typedefs.P("k%s = %d,", internal.ToUpperCamel(*field.Name), *field.Number)
}
cpp.Typedefs.PD("};")
cpp.Typedefs.P("%s %s = %s::%s_NOT_SET;", typeName, fieldName, typeName, upperName)
cpp.Typedefs.P("%s %s = %s::NOT_SET;", typeName, fieldName, typeName)
cpp.Typedefs.PI("void clear_%s() {", fieldName)
cpp.Typedefs.P("%s = %s::%s_NOT_SET;", fieldName, typeName, upperName)
cpp.Typedefs.P("%s = %s::NOT_SET;", fieldName, typeName)
for _, field := range fields {
fieldType, _, err := toTypeName(field)
if err != nil {
Expand Down Expand Up @@ -200,7 +199,7 @@ func genOneof(oneof *descriptorpb.OneofDescriptorProto, fields []*descriptorpb.F
cpp.TagInvokes.Deindent()
cpp.TagInvokes.P("default:")
cpp.TagInvokes.Indent()
cpp.TagInvokes.P("jv = (int)%s::%s_NOT_SET;", qName, upperName)
cpp.TagInvokes.P("jv = (int)%s::NOT_SET;", qName)
cpp.TagInvokes.P("break;")
cpp.TagInvokes.Deindent()
cpp.TagInvokes.PD("}")
Expand Down Expand Up @@ -231,13 +230,13 @@ func genEquals(desc *descriptorpb.DescriptorProto, pkg *string, parents []*descr
}
}
// oneof の比較
for _, oneof := range desc.OneofDecl {
for i, oneof := range desc.OneofDecl {
oneofFieldName := internal.ToSnakeCase(*oneof.Name) + "_case"
oneofTypeName := internal.ToUpperCamel(*oneof.Name) + "Case"
cpp.Typedefs.P("if (a.%s != b.%s) return false;", oneofFieldName, oneofFieldName)

for _, field := range desc.Field {
if field.OneofIndex != nil && *field.OneofIndex == *field.OneofIndex {
if field.OneofIndex != nil && *field.OneofIndex == int32(i) {
fieldName := internal.ToSnakeCase(*field.Name)
enumFieldName := internal.ToUpperCamel(*field.Name)
cpp.Typedefs.P("if (a.%s == %s::k%s && a.%s != b.%s) return false;",
Expand Down Expand Up @@ -575,6 +574,7 @@ func genFile(file *descriptorpb.FileDescriptorProto, files []*descriptorpb.FileD

func gen(req *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse, error) {
resp := &pluginpb.CodeGeneratorResponse{}
resp.SupportedFeatures = proto.Uint64(uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL))
for _, file := range req.ProtoFile {
respFile, err := genFile(file, req.ProtoFile)
if err != nil {
Expand Down
15 changes: 8 additions & 7 deletions cmd/protoc-gen-jsonif-unity/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/melpon/protoc-gen-jsonif/cmd/internal"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/pluginpb"
)
Expand Down Expand Up @@ -102,19 +103,18 @@ func genEnum(enum *descriptorpb.EnumDescriptorProto, pkg *string, parents []*des
func genOneof(oneof *descriptorpb.OneofDescriptorProto, fields []*descriptorpb.FieldDescriptorProto, pkg *string, parents []*descriptorpb.DescriptorProto, u *unityFile) error {
typeName := internal.ToUpperCamel(*oneof.Name) + "Case"
fieldName := internal.ToSnakeCase(*oneof.Name) + "_case"
upperName := strings.ToUpper(internal.ToSnakeCase(*oneof.Name))
u.Typedefs.P("[System.Serializable]")
u.Typedefs.P("public enum %s", typeName)
u.Typedefs.PI("{")
u.Typedefs.P("%s_NOT_SET = 0,", upperName)
u.Typedefs.P("NOT_SET = 0,")
for _, field := range fields {
u.Typedefs.P("k%s = %d,", internal.ToUpperCamel(*field.Name), *field.Number)
}
u.Typedefs.PD("}")
u.Typedefs.P("public %s %s;", typeName, fieldName)
u.Typedefs.P("public void Clear%s()", typeName)
u.Typedefs.PI("{")
u.Typedefs.P("%s = %s.%s_NOT_SET;", fieldName, typeName, upperName)
u.Typedefs.P("%s = %s.NOT_SET;", fieldName, typeName)
for _, field := range fields {
fieldType, defaultValue, err := toTypeName(field)
if err != nil {
Expand Down Expand Up @@ -149,13 +149,13 @@ func genEquals(desc *descriptorpb.DescriptorProto, pkg *string, parents []*descr
}
}
// oneof の比較
for _, oneof := range desc.OneofDecl {
for i, oneof := range desc.OneofDecl {
oneofFieldName := internal.ToSnakeCase(*oneof.Name) + "_case"
oneofTypeName := internal.ToUpperCamel(*oneof.Name) + "Case"
u.Typedefs.P("if (!this.%s.Equals(v.%s)) return false;", oneofFieldName, oneofFieldName)

for _, field := range desc.Field {
if field.OneofIndex != nil && *field.OneofIndex == *field.OneofIndex {
if field.OneofIndex != nil && *field.OneofIndex == int32(i) {
fieldName := internal.ToSnakeCase(*field.Name)
enumFieldName := internal.ToUpperCamel(*field.Name)
u.Typedefs.P("if (this.%s == %s.k%s && !this.%s.Equals(v.%s)) return false;",
Expand Down Expand Up @@ -183,13 +183,13 @@ func genEquals(desc *descriptorpb.DescriptorProto, pkg *string, parents []*descr
}
}
// oneof のハッシュ値
for _, oneof := range desc.OneofDecl {
for i, oneof := range desc.OneofDecl {
oneofFieldName := internal.ToSnakeCase(*oneof.Name) + "_case"
oneofTypeName := internal.ToUpperCamel(*oneof.Name) + "Case"
u.Typedefs.P("hashcode = hashcode * 7302013 ^ %s.GetHashCode();", oneofFieldName)

for _, field := range desc.Field {
if field.OneofIndex != nil && *field.OneofIndex == *field.OneofIndex {
if field.OneofIndex != nil && *field.OneofIndex == int32(i) {
fieldName := internal.ToSnakeCase(*field.Name)
enumFieldName := internal.ToUpperCamel(*field.Name)
u.Typedefs.P("if (%s == %s.k%s) hashcode = hashcode * 7302013 ^ %s.GetHashCode();",
Expand Down Expand Up @@ -346,6 +346,7 @@ func genJsonif() (*pluginpb.CodeGeneratorResponse_File, error) {

func gen(req *pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorResponse, error) {
resp := &pluginpb.CodeGeneratorResponse{}
resp.SupportedFeatures = proto.Uint64(uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL))
for _, file := range req.ProtoFile {
respFile, err := genFile(file)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pushd test/proto
size.proto \
jsonfield.proto \
optimistic.proto \
optional.proto \
discard_if_default.proto \
no_serializer.proto
$INSTALL_DIR/protoc/bin/protoc \
Expand All @@ -47,6 +48,7 @@ pushd test/proto
message.proto \
nested.proto \
oneof.proto \
optional.proto \
repeated.proto \
size.proto
$INSTALL_DIR/protoc/bin/protoc \
Expand All @@ -58,6 +60,7 @@ pushd test/proto
message.proto \
nested.proto \
oneof.proto \
optional.proto \
repeated.proto
popd

Expand Down
56 changes: 56 additions & 0 deletions test/c/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "nested.json.c.h"
#include "repeated.json.c.h"
#include "oneof.json.c.h"
#include "optional.json.c.h"
#include "importing.json.c.h"
#include "bytes.json.c.h"
#include "size.json.c.h"
Expand Down Expand Up @@ -211,6 +212,60 @@ void test_oneof() {
oneof_Test_destroy(&b);
}

void test_optional() {
optional_Test a;
optional_Test_init(&a);
optional_Test b;
optional_Test_init(&b);
assert(a._a_case == optional_Test_ACase_NOT_SET);
TEST_IDENTIFY(optional_Test, &a, &b);
assert(b._a_case == optional_Test_ACase_NOT_SET);

optional_Test_set_a(&a, 1);
assert(a._a_case == optional_Test_ACase_kA);
assert(a.a == 1);
TEST_IDENTIFY(optional_Test, &a, &b);
assert(b._a_case == optional_Test_ACase_kA);
assert(b.a == 1);

optional_Test_set_b(&a, "foo");
assert(a._b_case == optional_Test_BCase_kB);
assert(a.b_len == 3 && strcmp(a.b, "foo") == 0);
TEST_IDENTIFY(optional_Test, &a, &b);
assert(b._b_case == optional_Test_BCase_kB);
assert(b.b_len == 3 && strcmp(b.b, "foo") == 0);

optional_Test_set_c(&a, optional_BAR);
assert(a._c_case == optional_Test_CCase_kC);
assert(a.c == optional_BAR);
TEST_IDENTIFY(optional_Test, &a, &b);
assert(b._c_case == optional_Test_CCase_kC);
assert(b.c == optional_BAR);

optional_Message m;
optional_Message_init(&m);
optional_Message_set_name(&m, "bar");
optional_Test_set_d(&a, &m);
optional_Message_destroy(&m);
assert(a._d_case == optional_Test_DCase_kD);
assert(a.d.name_len == 3 && strcmp(a.d.name, "bar") == 0);
TEST_IDENTIFY(optional_Test, &a, &b);
assert(b._d_case == optional_Test_DCase_kD);
assert(b.d.name_len == 3 && strcmp(b.d.name, "bar") == 0);

optional_Test_clear__a_case(&a);
assert(a._a_case == optional_Test_ACase_NOT_SET);
optional_Test_clear__b_case(&a);
assert(a._b_case == optional_Test_BCase_NOT_SET);
optional_Test_clear__c_case(&a);
assert(a._c_case == optional_Test_CCase_NOT_SET);
optional_Test_clear__d_case(&a);
assert(a._d_case == optional_Test_DCase_NOT_SET);

optional_Test_destroy(&a);
optional_Test_destroy(&b);
}

void test_importing() {
importing_Test a;
importing_Test_init(&a);
Expand Down Expand Up @@ -257,6 +312,7 @@ int main() {
test_nested();
test_repeated();
test_oneof();
test_optional();
test_importing();
test_bytes();
test_size();
Expand Down
58 changes: 55 additions & 3 deletions test/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "nested.json.h"
#include "repeated.json.h"
#include "oneof.json.h"
#include "optional.json.h"
#include "importing.json.h"
#include "bytes.json.h"
#include "jsonfield.json.h"
Expand Down Expand Up @@ -102,9 +103,9 @@ void test_repeated() {

void test_oneof() {
oneof::Test a;
assert(a.test_oneof_case == oneof::Test::TestOneofCase::TEST_ONEOF_NOT_SET);
assert(a.test_oneof_case == oneof::Test::TestOneofCase::NOT_SET);
a = identify(a);
assert(a.test_oneof_case == oneof::Test::TestOneofCase::TEST_ONEOF_NOT_SET);
assert(a.test_oneof_case == oneof::Test::TestOneofCase::NOT_SET);

a.set_a(1);
assert(a.test_oneof_case == oneof::Test::TestOneofCase::kA);
Expand Down Expand Up @@ -135,7 +136,57 @@ void test_oneof() {
assert(a.d.name == "bar");

a.clear_test_oneof_case();
assert(a.test_oneof_case == oneof::Test::TestOneofCase::TEST_ONEOF_NOT_SET);
assert(a.test_oneof_case == oneof::Test::TestOneofCase::NOT_SET);
}

void test_optional() {
optional::Test a;
assert(a._a_case == optional::Test::ACase::NOT_SET);
assert(a._b_case == optional::Test::BCase::NOT_SET);
assert(a._c_case == optional::Test::CCase::NOT_SET);
assert(a._d_case == optional::Test::DCase::NOT_SET);
a = identify(a);
assert(a._a_case == optional::Test::ACase::NOT_SET);
assert(a._b_case == optional::Test::BCase::NOT_SET);
assert(a._c_case == optional::Test::CCase::NOT_SET);
assert(a._d_case == optional::Test::DCase::NOT_SET);

a.set_a(1);
assert(a._a_case == optional::Test::ACase::kA);
assert(a.a == 1);
a = identify(a);
assert(a._a_case == optional::Test::ACase::kA);
assert(a.a == 1);

a.set_b("foo");
assert(a._b_case == optional::Test::BCase::kB);
assert(a.b == "foo");
a = identify(a);
assert(a._b_case == optional::Test::BCase::kB);
assert(a.b == "foo");

a.set_c(optional::BAR);
assert(a._c_case == optional::Test::CCase::kC);
assert(a.c == optional::BAR);
a = identify(a);
assert(a._c_case == optional::Test::CCase::kC);
assert(a.c == optional::BAR);

a.set_d(optional::Message{"bar"});
assert(a._d_case == optional::Test::DCase::kD);
assert(a.d.name == "bar");
a = identify(a);
assert(a._d_case == optional::Test::DCase::kD);
assert(a.d.name == "bar");

a.clear__a_case();
assert(a._a_case == optional::Test::ACase::NOT_SET);
a.clear__b_case();
assert(a._b_case == optional::Test::BCase::NOT_SET);
a.clear__c_case();
assert(a._c_case == optional::Test::CCase::NOT_SET);
a.clear__d_case();
assert(a._d_case == optional::Test::DCase::NOT_SET);
}

void test_importing() {
Expand Down Expand Up @@ -240,6 +291,7 @@ int main() {
test_nested();
test_repeated();
test_oneof();
test_optional();
test_importing();
test_bytes();
test_jsonfield();
Expand Down
18 changes: 18 additions & 0 deletions test/proto/optional.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
syntax = "proto3";

package optional;

enum Enum {
FOO = 0;
BAR = 1;
}

message Message {
string name = 1;
}
message Test {
optional int64 a = 1;
optional string b = 3;
optional Enum c = 4;
optional Message d = 5;
}
Loading

0 comments on commit 75ef89d

Please sign in to comment.