1// Copyright 2010 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
5package proto
6
7import (
8	"fmt"
9	"reflect"
10	"strconv"
11	"strings"
12	"sync"
13
14	"google.golang.org/protobuf/reflect/protoreflect"
15	"google.golang.org/protobuf/runtime/protoimpl"
16)
17
18// StructProperties represents protocol buffer type information for a
19// generated protobuf message in the open-struct API.
20//
21// Deprecated: Do not use.
22type StructProperties struct {
23	// Prop are the properties for each field.
24	//
25	// Fields belonging to a oneof are stored in OneofTypes instead, with a
26	// single Properties representing the parent oneof held here.
27	//
28	// The order of Prop matches the order of fields in the Go struct.
29	// Struct fields that are not related to protobufs have a "XXX_" prefix
30	// in the Properties.Name and must be ignored by the user.
31	Prop []*Properties
32
33	// OneofTypes contains information about the oneof fields in this message.
34	// It is keyed by the protobuf field name.
35	OneofTypes map[string]*OneofProperties
36}
37
38// Properties represents the type information for a protobuf message field.
39//
40// Deprecated: Do not use.
41type Properties struct {
42	// Name is a placeholder name with little meaningful semantic value.
43	// If the name has an "XXX_" prefix, the entire Properties must be ignored.
44	Name string
45	// OrigName is the protobuf field name or oneof name.
46	OrigName string
47	// JSONName is the JSON name for the protobuf field.
48	JSONName string
49	// Enum is a placeholder name for enums.
50	// For historical reasons, this is neither the Go name for the enum,
51	// nor the protobuf name for the enum.
52	Enum string // Deprecated: Do not use.
53	// Weak contains the full name of the weakly referenced message.
54	Weak string
55	// Wire is a string representation of the wire type.
56	Wire string
57	// WireType is the protobuf wire type for the field.
58	WireType int
59	// Tag is the protobuf field number.
60	Tag int
61	// Required reports whether this is a required field.
62	Required bool
63	// Optional reports whether this is a optional field.
64	Optional bool
65	// Repeated reports whether this is a repeated field.
66	Repeated bool
67	// Packed reports whether this is a packed repeated field of scalars.
68	Packed bool
69	// Proto3 reports whether this field operates under the proto3 syntax.
70	Proto3 bool
71	// Oneof reports whether this field belongs within a oneof.
72	Oneof bool
73
74	// Default is the default value in string form.
75	Default string
76	// HasDefault reports whether the field has a default value.
77	HasDefault bool
78
79	// MapKeyProp is the properties for the key field for a map field.
80	MapKeyProp *Properties
81	// MapValProp is the properties for the value field for a map field.
82	MapValProp *Properties
83}
84
85// OneofProperties represents the type information for a protobuf oneof.
86//
87// Deprecated: Do not use.
88type OneofProperties struct {
89	// Type is a pointer to the generated wrapper type for the field value.
90	// This is nil for messages that are not in the open-struct API.
91	Type reflect.Type
92	// Field is the index into StructProperties.Prop for the containing oneof.
93	Field int
94	// Prop is the properties for the field.
95	Prop *Properties
96}
97
98// String formats the properties in the protobuf struct field tag style.
99func (p *Properties) String() string {
100	s := p.Wire
101	s += "," + strconv.Itoa(p.Tag)
102	if p.Required {
103		s += ",req"
104	}
105	if p.Optional {
106		s += ",opt"
107	}
108	if p.Repeated {
109		s += ",rep"
110	}
111	if p.Packed {
112		s += ",packed"
113	}
114	s += ",name=" + p.OrigName
115	if p.JSONName != "" {
116		s += ",json=" + p.JSONName
117	}
118	if len(p.Enum) > 0 {
119		s += ",enum=" + p.Enum
120	}
121	if len(p.Weak) > 0 {
122		s += ",weak=" + p.Weak
123	}
124	if p.Proto3 {
125		s += ",proto3"
126	}
127	if p.Oneof {
128		s += ",oneof"
129	}
130	if p.HasDefault {
131		s += ",def=" + p.Default
132	}
133	return s
134}
135
136// Parse populates p by parsing a string in the protobuf struct field tag style.
137func (p *Properties) Parse(tag string) {
138	// For example: "bytes,49,opt,name=foo,def=hello!"
139	for len(tag) > 0 {
140		i := strings.IndexByte(tag, ',')
141		if i < 0 {
142			i = len(tag)
143		}
144		switch s := tag[:i]; {
145		case strings.HasPrefix(s, "name="):
146			p.OrigName = s[len("name="):]
147		case strings.HasPrefix(s, "json="):
148			p.JSONName = s[len("json="):]
149		case strings.HasPrefix(s, "enum="):
150			p.Enum = s[len("enum="):]
151		case strings.HasPrefix(s, "weak="):
152			p.Weak = s[len("weak="):]
153		case strings.Trim(s, "0123456789") == "":
154			n, _ := strconv.ParseUint(s, 10, 32)
155			p.Tag = int(n)
156		case s == "opt":
157			p.Optional = true
158		case s == "req":
159			p.Required = true
160		case s == "rep":
161			p.Repeated = true
162		case s == "varint" || s == "zigzag32" || s == "zigzag64":
163			p.Wire = s
164			p.WireType = WireVarint
165		case s == "fixed32":
166			p.Wire = s
167			p.WireType = WireFixed32
168		case s == "fixed64":
169			p.Wire = s
170			p.WireType = WireFixed64
171		case s == "bytes":
172			p.Wire = s
173			p.WireType = WireBytes
174		case s == "group":
175			p.Wire = s
176			p.WireType = WireStartGroup
177		case s == "packed":
178			p.Packed = true
179		case s == "proto3":
180			p.Proto3 = true
181		case s == "oneof":
182			p.Oneof = true
183		case strings.HasPrefix(s, "def="):
184			// The default tag is special in that everything afterwards is the
185			// default regardless of the presence of commas.
186			p.HasDefault = true
187			p.Default, i = tag[len("def="):], len(tag)
188		}
189		tag = strings.TrimPrefix(tag[i:], ",")
190	}
191}
192
193// Init populates the properties from a protocol buffer struct tag.
194//
195// Deprecated: Do not use.
196func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
197	p.Name = name
198	p.OrigName = name
199	if tag == "" {
200		return
201	}
202	p.Parse(tag)
203
204	if typ != nil && typ.Kind() == reflect.Map {
205		p.MapKeyProp = new(Properties)
206		p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
207		p.MapValProp = new(Properties)
208		p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
209	}
210}
211
212var propertiesCache sync.Map // map[reflect.Type]*StructProperties
213
214// GetProperties returns the list of properties for the type represented by t,
215// which must be a generated protocol buffer message in the open-struct API,
216// where protobuf message fields are represented by exported Go struct fields.
217//
218// Deprecated: Use protobuf reflection instead.
219func GetProperties(t reflect.Type) *StructProperties {
220	if p, ok := propertiesCache.Load(t); ok {
221		return p.(*StructProperties)
222	}
223	p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
224	return p.(*StructProperties)
225}
226
227func newProperties(t reflect.Type) *StructProperties {
228	if t.Kind() != reflect.Struct {
229		panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
230	}
231
232	var hasOneof bool
233	prop := new(StructProperties)
234
235	// Construct a list of properties for each field in the struct.
236	for i := 0; i < t.NumField(); i++ {
237		p := new(Properties)
238		f := t.Field(i)
239		tagField := f.Tag.Get("protobuf")
240		p.Init(f.Type, f.Name, tagField, &f)
241
242		tagOneof := f.Tag.Get("protobuf_oneof")
243		if tagOneof != "" {
244			hasOneof = true
245			p.OrigName = tagOneof
246		}
247
248		// Rename unrelated struct fields with the "XXX_" prefix since so much
249		// user code simply checks for this to exclude special fields.
250		if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
251			p.Name = "XXX_" + p.Name
252			p.OrigName = "XXX_" + p.OrigName
253		} else if p.Weak != "" {
254			p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
255		}
256
257		prop.Prop = append(prop.Prop, p)
258	}
259
260	// Construct a mapping of oneof field names to properties.
261	if hasOneof {
262		var oneofWrappers []interface{}
263		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
264			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
265		}
266		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
267			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
268		}
269		if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
270			if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
271				oneofWrappers = m.ProtoMessageInfo().OneofWrappers
272			}
273		}
274
275		prop.OneofTypes = make(map[string]*OneofProperties)
276		for _, wrapper := range oneofWrappers {
277			p := &OneofProperties{
278				Type: reflect.ValueOf(wrapper).Type(), // *T
279				Prop: new(Properties),
280			}
281			f := p.Type.Elem().Field(0)
282			p.Prop.Name = f.Name
283			p.Prop.Parse(f.Tag.Get("protobuf"))
284
285			// Determine the struct field that contains this oneof.
286			// Each wrapper is assignable to exactly one parent field.
287			var foundOneof bool
288			for i := 0; i < t.NumField() && !foundOneof; i++ {
289				if p.Type.AssignableTo(t.Field(i).Type) {
290					p.Field = i
291					foundOneof = true
292				}
293			}
294			if !foundOneof {
295				panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
296			}
297			prop.OneofTypes[p.Prop.OrigName] = p
298		}
299	}
300
301	return prop
302}
303
304func (sp *StructProperties) Len() int           { return len(sp.Prop) }
305func (sp *StructProperties) Less(i, j int) bool { return false }
306func (sp *StructProperties) Swap(i, j int)      { return }
307