xref: /aosp_15_r20/external/golang-protobuf/internal/protobuild/build.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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