1// Copyright 2020 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package protobuild constructs messages. 6// 7// This package is used to construct multiple types of message with a similar shape 8// from a common template. 9package protobuild 10 11import ( 12 "fmt" 13 "math" 14 "reflect" 15 16 "google.golang.org/protobuf/reflect/protoreflect" 17 "google.golang.org/protobuf/reflect/protoregistry" 18) 19 20// A Value is a value assignable to a field. 21// A Value may be a value accepted by protoreflect.ValueOf. In addition: 22// 23// • An int may be assigned to any numeric field. 24// 25// • A float64 may be assigned to a double field. 26// 27// • Either a string or []byte may be assigned to a string or bytes field. 28// 29// • A string containing the value name may be assigned to an enum field. 30// 31// • A slice may be assigned to a list, and a map may be assigned to a map. 32type Value interface{} 33 34// A Message is a template to apply to a message. Keys are field names, including 35// extension names. 36type Message map[protoreflect.Name]Value 37 38// Unknown is a key associated with the unknown fields of a message. 39// The value should be a []byte. 40const Unknown = "@unknown" 41 42// Build applies the template to a message. 43func (template Message) Build(m protoreflect.Message) { 44 md := m.Descriptor() 45 fields := md.Fields() 46 exts := make(map[protoreflect.Name]protoreflect.FieldDescriptor) 47 protoregistry.GlobalTypes.RangeExtensionsByMessage(md.FullName(), func(xt protoreflect.ExtensionType) bool { 48 xd := xt.TypeDescriptor() 49 exts[xd.Name()] = xd 50 return true 51 }) 52 for k, v := range template { 53 if k == Unknown { 54 m.SetUnknown(protoreflect.RawFields(v.([]byte))) 55 continue 56 } 57 fd := fields.ByName(k) 58 if fd == nil { 59 fd = exts[k] 60 } 61 if fd == nil { 62 panic(fmt.Sprintf("%v.%v: not found", md.FullName(), k)) 63 } 64 switch { 65 case fd.IsList(): 66 list := m.Mutable(fd).List() 67 s := reflect.ValueOf(v) 68 for i := 0; i < s.Len(); i++ { 69 if fd.Message() == nil { 70 list.Append(fieldValue(fd, s.Index(i).Interface())) 71 } else { 72 e := list.NewElement() 73 s.Index(i).Interface().(Message).Build(e.Message()) 74 list.Append(e) 75 } 76 } 77 case fd.IsMap(): 78 mapv := m.Mutable(fd).Map() 79 rm := reflect.ValueOf(v) 80 for _, k := range rm.MapKeys() { 81 mk := fieldValue(fd.MapKey(), k.Interface()).MapKey() 82 if fd.MapValue().Message() == nil { 83 mv := fieldValue(fd.MapValue(), rm.MapIndex(k).Interface()) 84 mapv.Set(mk, mv) 85 } else if mapv.Has(mk) { 86 mv := mapv.Get(mk).Message() 87 rm.MapIndex(k).Interface().(Message).Build(mv) 88 } else { 89 mv := mapv.NewValue() 90 rm.MapIndex(k).Interface().(Message).Build(mv.Message()) 91 mapv.Set(mk, mv) 92 } 93 } 94 default: 95 if fd.Message() == nil { 96 m.Set(fd, fieldValue(fd, v)) 97 } else { 98 v.(Message).Build(m.Mutable(fd).Message()) 99 } 100 } 101 } 102} 103 104func fieldValue(fd protoreflect.FieldDescriptor, v interface{}) protoreflect.Value { 105 switch o := v.(type) { 106 case int: 107 switch fd.Kind() { 108 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: 109 if o < math.MinInt32 || math.MaxInt32 < o { 110 panic(fmt.Sprintf("%v: value %v out of range [%v, %v]", fd.FullName(), o, int32(math.MinInt32), int32(math.MaxInt32))) 111 } 112 v = int32(o) 113 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: 114 if o < 0 || math.MaxUint32 < 0 { 115 panic(fmt.Sprintf("%v: value %v out of range [%v, %v]", fd.FullName(), o, uint32(0), uint32(math.MaxUint32))) 116 } 117 v = uint32(o) 118 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: 119 v = int64(o) 120 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: 121 if o < 0 { 122 panic(fmt.Sprintf("%v: value %v out of range [%v, %v]", fd.FullName(), o, uint64(0), uint64(math.MaxUint64))) 123 } 124 v = uint64(o) 125 case protoreflect.FloatKind: 126 v = float32(o) 127 case protoreflect.DoubleKind: 128 v = float64(o) 129 case protoreflect.EnumKind: 130 v = protoreflect.EnumNumber(o) 131 default: 132 panic(fmt.Sprintf("%v: invalid value type int", fd.FullName())) 133 } 134 case float64: 135 switch fd.Kind() { 136 case protoreflect.FloatKind: 137 v = float32(o) 138 } 139 case string: 140 switch fd.Kind() { 141 case protoreflect.BytesKind: 142 v = []byte(o) 143 case protoreflect.EnumKind: 144 v = fd.Enum().Values().ByName(protoreflect.Name(o)).Number() 145 } 146 case []byte: 147 return protoreflect.ValueOf(append([]byte{}, o...)) 148 } 149 return protoreflect.ValueOf(v) 150} 151