-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfield_access.go
More file actions
91 lines (82 loc) · 2.58 KB
/
Copy pathfield_access.go
File metadata and controls
91 lines (82 loc) · 2.58 KB
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
package protopath
import (
"context"
"fmt"
"reflect"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
// FieldAccessOperation is one of jsonpath operation that access field given the object
type FieldAccessOperation struct {
// fieldName is name of field that will be retrieved from the given object
fieldName string
// fieldGetter is way of accessing field, e.g accessing field from protobuf message
fieldGetter FieldGetter
// fromRoot indicate that access field from root object not the current object
fromRoot bool
}
// Lookup evaluate operation and returning field value
func (f *FieldAccessOperation) Lookup(ctx context.Context, obj, rootObj any) (any, error) {
selectedObj := obj
if f.fromRoot {
selectedObj = rootObj
}
return f.fieldGetter.GetField(f.fieldName, selectedObj)
}
// FieldGetter get value of field given the field name and object to evaluate
type FieldGetter interface {
GetField(name string, obj any) (any, error)
}
// ProtoFieldGetter is way to access field for protobuf message object
type ProtoFieldGetter struct{}
// GetField retrieve the field value given the the field name and object
func (p *ProtoFieldGetter) GetField(fieldName string, obj any) (any, error) {
objReflectVal := reflect.ValueOf(obj)
switch objReflectVal.Kind() {
case reflect.Slice:
res := make([]any, 0, objReflectVal.Len())
for i := 0; i < objReflectVal.Len(); i++ {
val, err := p.getFieldFromMessage(fieldName, objReflectVal.Index(i).Interface())
if err != nil {
return nil, err
}
res = append(res, val)
}
return res, nil
default:
return p.getFieldFromMessage(fieldName, obj)
}
}
func (p *ProtoFieldGetter) getFieldFromMessage(fieldName string, obj any) (any, error) {
protoObj, isProto := obj.(proto.Message)
if !isProto {
return nil, fmt.Errorf("object is not in proto.Message type")
}
messageReflect := protoObj.ProtoReflect()
fieldDesciptor := messageReflect.Descriptor().Fields().ByName(protoreflect.Name(fieldName))
if fieldDesciptor == nil {
return nil, nil
}
fieldValue := messageReflect.Get(fieldDesciptor).Interface()
switch fieldT := fieldValue.(type) {
case protoreflect.Message:
if !fieldT.IsValid() {
return nil, nil
}
return fieldT.Interface(), nil
case protoreflect.List:
result := make([]any, 0, fieldT.Len())
for i := 0; i < fieldT.Len(); i++ {
val := fieldT.Get(i).Interface()
switch valT := val.(type) {
case protoreflect.Message:
result = append(result, valT.Interface())
default:
result = append(result, valT)
}
}
return result, nil
default:
return fieldValue, nil
}
}