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