-
Notifications
You must be signed in to change notification settings - Fork 168
/
proto3_optional.go
75 lines (70 loc) · 2.44 KB
/
proto3_optional.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package internal
import (
"strings"
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
)
// ProcessProto3OptionalFields adds synthetic oneofs to the given message descriptor
// for each proto3 optional field. It also updates the fields to have the correct
// oneof index reference. The given callback, if not nil, is called for each synthetic
// oneof created.
func ProcessProto3OptionalFields(msgd *descriptorpb.DescriptorProto, callback func(*descriptorpb.FieldDescriptorProto, *descriptorpb.OneofDescriptorProto)) {
var allNames map[string]struct{}
for _, fd := range msgd.Field {
if fd.GetProto3Optional() {
// lazy init the set of all names
if allNames == nil {
allNames = map[string]struct{}{}
for _, fd := range msgd.Field {
allNames[fd.GetName()] = struct{}{}
}
for _, od := range msgd.OneofDecl {
allNames[od.GetName()] = struct{}{}
}
// NB: protoc only considers names of other fields and oneofs
// when computing the synthetic oneof name. But that feels like
// a bug, since it means it could generate a name that conflicts
// with some other symbol defined in the message. If it's decided
// that's NOT a bug and is desirable, then we should remove the
// following four loops to mimic protoc's behavior.
for _, xd := range msgd.Extension {
allNames[xd.GetName()] = struct{}{}
}
for _, ed := range msgd.EnumType {
allNames[ed.GetName()] = struct{}{}
for _, evd := range ed.Value {
allNames[evd.GetName()] = struct{}{}
}
}
for _, fd := range msgd.NestedType {
allNames[fd.GetName()] = struct{}{}
}
for _, n := range msgd.ReservedName {
allNames[n] = struct{}{}
}
}
// Compute a name for the synthetic oneof. This uses the same
// algorithm as used in protoc:
// https://github.com/protocolbuffers/protobuf/blob/74ad62759e0a9b5a21094f3fb9bb4ebfaa0d1ab8/src/google/protobuf/compiler/parser.cc#L785-L803
ooName := fd.GetName()
if !strings.HasPrefix(ooName, "_") {
ooName = "_" + ooName
}
for {
_, ok := allNames[ooName]
if !ok {
// found a unique name
allNames[ooName] = struct{}{}
break
}
ooName = "X" + ooName
}
fd.OneofIndex = proto.Int32(int32(len(msgd.OneofDecl)))
ood := &descriptorpb.OneofDescriptorProto{Name: proto.String(ooName)}
msgd.OneofDecl = append(msgd.OneofDecl, ood)
if callback != nil {
callback(fd, ood)
}
}
}
}