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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
package protoregistry
import (
"fmt"
"reflect"
"regexp"
"strconv"
"github.com/golang/protobuf/proto"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
const (
protobufTag = "protobuf"
protobufOneOfTag = "protobuf_oneof"
)
// reflectFindRepoTarget finds the target repository by using the OID to
// navigate the struct tags
// Warning: this reflection filled function is full of forbidden dark elf magic
func reflectFindRepoTarget(pbMsg proto.Message, targetOID []int) (*gitalypb.Repository, error) {
var targetRepo *gitalypb.Repository
msgV := reflect.ValueOf(pbMsg)
for _, fieldNo := range targetOID {
var err error
msgV, err = findProtoField(msgV, fieldNo)
if err != nil {
return nil, fmt.Errorf(
"unable to descend OID %+v into message %s: %s",
targetOID, proto.MessageName(pbMsg), err,
)
}
}
targetRepo, ok := msgV.Interface().(*gitalypb.Repository)
if !ok {
return nil, fmt.Errorf("repo target OID %v points to non-Repo type %+v", targetOID, msgV.Interface())
}
return targetRepo, nil
}
// matches a tag string like "bytes,1,opt,name=repository,proto3"
var protobufTagRegex = regexp.MustCompile(`^(.*?),(\d+),(.*?),name=(.*?),proto3(\,oneof)?$`)
const (
protobufTagRegexGroups = 6
protobufTagRegexFieldGroup = 2
)
func findProtoField(msgV reflect.Value, protoField int) (reflect.Value, error) {
msgV = reflect.Indirect(msgV)
for i := 0; i < msgV.NumField(); i++ {
field := msgV.Type().Field(i)
ok, err := tryNumberedField(field, protoField)
if err != nil {
return reflect.Value{}, err
}
if ok {
return msgV.FieldByName(field.Name), nil
}
oneofField, ok := tryOneOfField(msgV, field, protoField)
if !ok {
continue
}
return oneofField, nil
}
err := fmt.Errorf(
"unable to find protobuf field %d in message %s",
protoField, msgV.Type().Name(),
)
return reflect.Value{}, err
}
func tryNumberedField(field reflect.StructField, protoField int) (bool, error) {
tag := field.Tag.Get(protobufTag)
matches := protobufTagRegex.FindStringSubmatch(tag)
if len(matches) == protobufTagRegexGroups {
fieldStr := matches[protobufTagRegexFieldGroup]
if fieldStr == strconv.Itoa(protoField) {
return true, nil
}
}
return false, nil
}
func tryOneOfField(msgV reflect.Value, field reflect.StructField, protoField int) (reflect.Value, bool) {
oneOfTag := field.Tag.Get(protobufOneOfTag)
if oneOfTag == "" {
return reflect.Value{}, false // empty tag means this is not a oneOf field
}
// try all of the oneOf fields until a match is found
msgV = msgV.FieldByName(field.Name).Elem().Elem()
for i := 0; i < msgV.NumField(); i++ {
field = msgV.Type().Field(i)
ok, err := tryNumberedField(field, protoField)
if err != nil {
return reflect.Value{}, false
}
if ok {
return msgV.FieldByName(field.Name), true
}
}
return reflect.Value{}, false
}
|