Files
micro-client-http/builder/helpers.go
pugnack 24801750a7
Some checks failed
coverage / build (push) Successful in 2m19s
test / test (push) Failing after 17m15s
integrate request builder into HTTP client for googleapis support (#157)
2025-09-23 13:30:15 +03:00

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())
}
}