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