Skip to content

Commit

Permalink
Fix Multiple External Type Usage in One Message (#8)
Browse files Browse the repository at this point in the history
* update example/user_add_comment.proto

* make created_at and updated_at optional

* avoid message definition conflicts

* update sample output

* revert the original name of a message

* make debts order predictable

* fix test
  • Loading branch information
alpancs authored Aug 15, 2021
1 parent 0f4382d commit 4cb7763
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 59 deletions.
84 changes: 59 additions & 25 deletions content_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"regexp"
"strings"

"google.golang.org/protobuf/types/descriptorpb"
Expand Down Expand Up @@ -33,44 +34,77 @@ func (b *contentBuilder) build(protoFile *descriptorpb.FileDescriptorProto) (str
}

func (b *contentBuilder) buildMessage(message *descriptorpb.DescriptorProto, level int) {
b.output.WriteString(buildIndent(level) + "message " + message.GetName() + " {\n")
fmt.Fprintf(b.output, "%smessage %s {\n", buildIndent(level), message.GetName())
debts := []string(nil)
for _, field := range message.GetField() {
b.buildField(field, level+1)
debts = append(debts, b.buildField(field, level+1))
}
b.output.WriteString(buildIndent(level) + "}\n")
b.payDebts(debts, level+1)
fmt.Fprintf(b.output, "%s}\n", buildIndent(level))
}

func (b *contentBuilder) buildField(field *descriptorpb.FieldDescriptorProto, level int) {
fieldType := strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_"))
if field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
fieldType = b.buildFieldType(field.GetTypeName(), level)
}
b.output.WriteString(buildIndent(level))
b.buildFieldLabel(field.GetLabel())
fmt.Fprintf(b.output, "%s %s = %d;\n", fieldType, field.GetName(), field.GetNumber())
func (b *contentBuilder) buildField(field *descriptorpb.FieldDescriptorProto, level int) string {
fieldType, debt := b.getFieldType(field)
fmt.Fprintf(b.output, "%s%s%s %s = %d;\n",
buildIndent(level),
b.getLabelPrefix(field.GetLabel()),
fieldType,
field.GetName(),
field.GetNumber(),
)
return debt
}

func (b *contentBuilder) buildFieldType(typeName string, level int) string {
if b.messageEncoding == "json" {
if typeName, ok := wktMapping[typeName]; ok {
return typeName
}
func (b *contentBuilder) getFieldType(field *descriptorpb.FieldDescriptorProto) (string, string) {
if field.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
return strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_")), ""
}

b.output.WriteString("\n")
b.buildMessage(b.messageTypes[typeName], level)
b.output.WriteString("\n")
return typeName[strings.LastIndexByte(typeName, '.')+1:]
fullMessageName := field.GetTypeName()
wkt := wktMapping[fullMessageName]
if b.messageEncoding == "json" && wkt != "" {
return wkt, ""
}
return getLocalName(fullMessageName), fullMessageName
}

func (b *contentBuilder) buildFieldLabel(label descriptorpb.FieldDescriptorProto_Label) {
func (b *contentBuilder) getLabelPrefix(label descriptorpb.FieldDescriptorProto_Label) string {
if label == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
b.output.WriteString("repeated ")
} else if b.schemaSyntax == "proto2" {
b.output.WriteString(strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")) + " ")
return "repeated "
}
if b.schemaSyntax == "proto2" {
return strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")) + " "
}
return ""
}

func (b *contentBuilder) payDebts(debts []string, level int) {
payedDebts := make(map[string]bool)
for _, debt := range debts {
if debt != "" && !payedDebts[debt] {
b.payDebt(debt, level)
payedDebts[debt] = true
}
}
}

func (b *contentBuilder) payDebt(debt string, level int) {
message := b.messageTypes[debt]
defer func(originalName *string) { message.Name = originalName }(message.Name)
localName := getLocalName(debt)
message.Name = &localName
b.output.WriteString("\n")
b.buildMessage(message, level)
}

func buildIndent(level int) string {
return strings.Repeat(" ", level)
}

var localNamePattern = regexp.MustCompile(`\..`)

func getLocalName(fullMessageName string) string {
return localNamePattern.ReplaceAllStringFunc(
fullMessageName,
func(s string) string { return strings.ToUpper(s[1:]) },
)
}
27 changes: 15 additions & 12 deletions example/user_add_comment.pps
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
syntax = "proto2";

message UserAddComment {
required ExampleUserAddCommentUser user = 1;
required string comment = 2;
repeated ExampleCommonLabel labels = 3;
required GoogleProtobufTimestamp timestamp = 101;

message User {
message ExampleUserAddCommentUser {
required string first_name = 1;
optional string last_name = 2;
optional bytes avatar = 3;
optional ExampleUserAddCommentUserLocation location = 4;
optional GoogleProtobufTimestamp created_at = 5;
optional GoogleProtobufTimestamp updated_at = 6;

message Location {
message ExampleUserAddCommentUserLocation {
required double longitude = 1;
required double latitude = 2;
}

optional Location location = 4;
message GoogleProtobufTimestamp {
optional int64 seconds = 1;
optional int32 nanos = 2;
}
}

required User user = 1;
required string comment = 2;

message Label {
message ExampleCommonLabel {
optional string key = 1;
optional string value = 2;
}

repeated Label labels = 3;

message Timestamp {
message GoogleProtobufTimestamp {
optional int64 seconds = 1;
optional int32 nanos = 2;
}

required Timestamp timestamp = 101;
}
16 changes: 8 additions & 8 deletions example/user_add_comment.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ import "example/common/label.proto";
import "google/protobuf/timestamp.proto";

message UserAddComment {
required User user = 1;
required string comment = 2;
repeated example.common.Label labels = 3;
required google.protobuf.Timestamp timestamp = 101;

message User {
required string first_name = 1;
optional string last_name = 2;
optional bytes avatar = 3;
optional Location location = 4;
optional google.protobuf.Timestamp created_at = 5;
optional google.protobuf.Timestamp updated_at = 6;

message Location {
required double longitude = 1;
required double latitude = 2;
}

optional Location location = 4;
}

required User user = 1;
required string comment = 2;
repeated example.common.Label labels = 3;

required google.protobuf.Timestamp timestamp = 101;
}
Binary file modified test/user_add_comment.in
Binary file not shown.
31 changes: 17 additions & 14 deletions test/user_add_comment.out
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
z�
example/user_add_comment.ppsz�syntax = "proto2";
z�
example/user_add_comment.ppsz�syntax = "proto2";

message UserAddComment {
required ExampleUserAddCommentUser user = 1;
required string comment = 2;
repeated ExampleCommonLabel labels = 3;
required GoogleProtobufTimestamp timestamp = 101;

message User {
message ExampleUserAddCommentUser {
required string first_name = 1;
optional string last_name = 2;
optional bytes avatar = 3;
optional ExampleUserAddCommentUserLocation location = 4;
optional GoogleProtobufTimestamp created_at = 5;
optional GoogleProtobufTimestamp updated_at = 6;

message Location {
message ExampleUserAddCommentUserLocation {
required double longitude = 1;
required double latitude = 2;
}

optional Location location = 4;
message GoogleProtobufTimestamp {
optional int64 seconds = 1;
optional int32 nanos = 2;
}
}

required User user = 1;
required string comment = 2;

message Label {
message ExampleCommonLabel {
optional string key = 1;
optional string value = 2;
}

repeated Label labels = 3;

message Timestamp {
message GoogleProtobufTimestamp {
optional int64 seconds = 1;
optional int32 nanos = 2;
}

required Timestamp timestamp = 101;
}

0 comments on commit 4cb7763

Please sign in to comment.