115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package builder
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
)
|
|
|
|
// findFieldByPath resolves a dot-separated field path in a protobuf message and returns the protoreflect value and its descriptor.
|
|
func findFieldByPath(msg protoreflect.Message, fieldPath string) (protoreflect.Value, protoreflect.FieldDescriptor, bool) {
|
|
var (
|
|
current = msg
|
|
parts = strings.Split(fieldPath, ".")
|
|
partsCount = len(parts) - 1
|
|
)
|
|
|
|
for i, part := range parts {
|
|
fd, ok := findFieldByName(current, part)
|
|
if !ok {
|
|
return protoreflect.Value{}, nil, false
|
|
}
|
|
|
|
val := current.Get(fd)
|
|
if i == partsCount { // it's last part
|
|
return val, fd, true
|
|
}
|
|
|
|
if fd.Kind() != protoreflect.MessageKind {
|
|
return protoreflect.Value{}, nil, false
|
|
}
|
|
current = val.Message()
|
|
}
|
|
|
|
return protoreflect.Value{}, nil, false
|
|
}
|
|
|
|
// findFieldByName find a field name in a protobuf message and returns the protoreflect field descriptor.
|
|
func findFieldByName(msg protoreflect.Message, fieldName string) (protoreflect.FieldDescriptor, bool) {
|
|
fields := msg.Descriptor().Fields()
|
|
for i := 0; i < fields.Len(); i++ {
|
|
fd := fields.Get(i)
|
|
if fd.JSONName() == fieldName {
|
|
return fd, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// isZeroValue checks if protoreflect.Value is zero for the field.
|
|
func isZeroValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) bool {
|
|
if fd.IsList() {
|
|
return val.List().Len() == 0
|
|
}
|
|
if fd.IsMap() {
|
|
return val.Map().Len() == 0
|
|
}
|
|
|
|
switch fd.Kind() {
|
|
case protoreflect.StringKind:
|
|
return val.String() == ""
|
|
case protoreflect.BytesKind:
|
|
return len(val.Bytes()) == 0
|
|
case protoreflect.BoolKind:
|
|
return !val.Bool()
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
|
|
protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
return val.Int() == 0
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind,
|
|
protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
return val.Uint() == 0
|
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
|
return val.Float() == 0
|
|
case protoreflect.EnumKind:
|
|
return val.Enum() == 0
|
|
case protoreflect.MessageKind:
|
|
return !val.Message().IsValid()
|
|
default:
|
|
return !val.IsValid()
|
|
}
|
|
}
|
|
|
|
// stringifyValue converts protoreflect.Value to string for path/query substitution.
|
|
func stringifyValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) (string, error) {
|
|
switch fd.Kind() {
|
|
case protoreflect.StringKind:
|
|
return val.String(), nil
|
|
case protoreflect.BytesKind:
|
|
return base64.StdEncoding.EncodeToString(val.Bytes()), nil
|
|
case protoreflect.BoolKind:
|
|
if val.Bool() {
|
|
return "true", nil
|
|
}
|
|
return "false", nil
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
|
|
protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
return fmt.Sprintf("%d", val.Int()), nil
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind,
|
|
protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
return fmt.Sprintf("%d", val.Uint()), nil
|
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
|
return strconv.FormatFloat(val.Float(), 'g', -1, 64), nil
|
|
case protoreflect.EnumKind:
|
|
ed := fd.Enum().Values().ByNumber(val.Enum())
|
|
if ed != nil {
|
|
return string(ed.Name()), nil
|
|
}
|
|
return fmt.Sprintf("%d", val.Enum()), nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported field kind: %s", fd.Kind())
|
|
}
|
|
}
|