blob: dcdc2202fada8b87c063dbc721c83f44a728dea4 [file] [log] [blame]
khenaidoo26721882021-08-11 17:42:52 -04001// 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.
Scott Baker2c1c4822019-10-16 11:02:41 -07004
5package proto
6
Scott Baker2c1c4822019-10-16 11:02:41 -07007import (
8 "fmt"
Scott Baker2c1c4822019-10-16 11:02:41 -07009 "reflect"
Scott Baker2c1c4822019-10-16 11:02:41 -070010 "strconv"
11 "strings"
12 "sync"
khenaidoo26721882021-08-11 17:42:52 -040013
14 "google.golang.org/protobuf/reflect/protoreflect"
15 "google.golang.org/protobuf/runtime/protoimpl"
Scott Baker2c1c4822019-10-16 11:02:41 -070016)
17
khenaidoo26721882021-08-11 17:42:52 -040018// StructProperties represents protocol buffer type information for a
19// generated protobuf message in the open-struct API.
20//
21// Deprecated: Do not use.
Scott Baker2c1c4822019-10-16 11:02:41 -070022type StructProperties struct {
khenaidoo26721882021-08-11 17:42:52 -040023 // 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
Scott Baker2c1c4822019-10-16 11:02:41 -070032
33 // OneofTypes contains information about the oneof fields in this message.
khenaidoo26721882021-08-11 17:42:52 -040034 // It is keyed by the protobuf field name.
Scott Baker2c1c4822019-10-16 11:02:41 -070035 OneofTypes map[string]*OneofProperties
36}
37
khenaidoo26721882021-08-11 17:42:52 -040038// Properties represents the type information for a protobuf message field.
39//
40// Deprecated: Do not use.
Scott Baker2c1c4822019-10-16 11:02:41 -070041type Properties struct {
khenaidoo26721882021-08-11 17:42:52 -040042 // 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.
Scott Baker2c1c4822019-10-16 11:02:41 -070058 WireType int
khenaidoo26721882021-08-11 17:42:52 -040059 // Tag is the protobuf field number.
60 Tag int
61 // Required reports whether this is a required field.
Scott Baker2c1c4822019-10-16 11:02:41 -070062 Required bool
khenaidoo26721882021-08-11 17:42:52 -040063 // Optional reports whether this is a optional field.
Scott Baker2c1c4822019-10-16 11:02:41 -070064 Optional bool
khenaidoo26721882021-08-11 17:42:52 -040065 // Repeated reports whether this is a repeated field.
Scott Baker2c1c4822019-10-16 11:02:41 -070066 Repeated bool
khenaidoo26721882021-08-11 17:42:52 -040067 // 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
Scott Baker2c1c4822019-10-16 11:02:41 -070073
khenaidoo26721882021-08-11 17:42:52 -040074 // Default is the default value in string form.
75 Default string
76 // HasDefault reports whether the field has a default value.
77 HasDefault bool
Scott Baker2c1c4822019-10-16 11:02:41 -070078
khenaidoo26721882021-08-11 17:42:52 -040079 // 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}
Scott Baker2c1c4822019-10-16 11:02:41 -070084
khenaidoo26721882021-08-11 17:42:52 -040085// 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
Scott Baker2c1c4822019-10-16 11:02:41 -070096}
97
98// String formats the properties in the protobuf struct field tag style.
99func (p *Properties) String() string {
100 s := p.Wire
khenaidoo26721882021-08-11 17:42:52 -0400101 s += "," + strconv.Itoa(p.Tag)
Scott Baker2c1c4822019-10-16 11:02:41 -0700102 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
khenaidoo26721882021-08-11 17:42:52 -0400115 if p.JSONName != "" {
Scott Baker2c1c4822019-10-16 11:02:41 -0700116 s += ",json=" + p.JSONName
117 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700118 if len(p.Enum) > 0 {
119 s += ",enum=" + p.Enum
120 }
khenaidoo26721882021-08-11 17:42:52 -0400121 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 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700130 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.
khenaidoo26721882021-08-11 17:42:52 -0400137func (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":
Scott Baker2c1c4822019-10-16 11:02:41 -0700157 p.Optional = true
khenaidoo26721882021-08-11 17:42:52 -0400158 case s == "req":
159 p.Required = true
160 case s == "rep":
Scott Baker2c1c4822019-10-16 11:02:41 -0700161 p.Repeated = true
khenaidoo26721882021-08-11 17:42:52 -0400162 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":
Scott Baker2c1c4822019-10-16 11:02:41 -0700178 p.Packed = true
khenaidoo26721882021-08-11 17:42:52 -0400179 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.
Scott Baker2c1c4822019-10-16 11:02:41 -0700186 p.HasDefault = true
khenaidoo26721882021-08-11 17:42:52 -0400187 p.Default, i = tag[len("def="):], len(tag)
Scott Baker2c1c4822019-10-16 11:02:41 -0700188 }
khenaidoo26721882021-08-11 17:42:52 -0400189 tag = strings.TrimPrefix(tag[i:], ",")
Scott Baker2c1c4822019-10-16 11:02:41 -0700190 }
191}
192
Scott Baker2c1c4822019-10-16 11:02:41 -0700193// Init populates the properties from a protocol buffer struct tag.
khenaidoo26721882021-08-11 17:42:52 -0400194//
195// Deprecated: Do not use.
Scott Baker2c1c4822019-10-16 11:02:41 -0700196func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
Scott Baker2c1c4822019-10-16 11:02:41 -0700197 p.Name = name
198 p.OrigName = name
199 if tag == "" {
200 return
201 }
202 p.Parse(tag)
khenaidoo26721882021-08-11 17:42:52 -0400203
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 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700210}
211
khenaidoo26721882021-08-11 17:42:52 -0400212var propertiesCache sync.Map // map[reflect.Type]*StructProperties
Scott Baker2c1c4822019-10-16 11:02:41 -0700213
khenaidoo26721882021-08-11 17:42:52 -0400214// 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.
Scott Baker2c1c4822019-10-16 11:02:41 -0700219func GetProperties(t reflect.Type) *StructProperties {
khenaidoo26721882021-08-11 17:42:52 -0400220 if p, ok := propertiesCache.Load(t); ok {
221 return p.(*StructProperties)
Scott Baker2c1c4822019-10-16 11:02:41 -0700222 }
khenaidoo26721882021-08-11 17:42:52 -0400223 p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
224 return p.(*StructProperties)
Scott Baker2c1c4822019-10-16 11:02:41 -0700225}
226
khenaidoo26721882021-08-11 17:42:52 -0400227func 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))
Scott Baker2c1c4822019-10-16 11:02:41 -0700230 }
231
khenaidoo26721882021-08-11 17:42:52 -0400232 var hasOneof bool
Scott Baker2c1c4822019-10-16 11:02:41 -0700233 prop := new(StructProperties)
Scott Baker2c1c4822019-10-16 11:02:41 -0700234
khenaidoo26721882021-08-11 17:42:52 -0400235 // Construct a list of properties for each field in the struct.
Scott Baker2c1c4822019-10-16 11:02:41 -0700236 for i := 0; i < t.NumField(); i++ {
Scott Baker2c1c4822019-10-16 11:02:41 -0700237 p := new(Properties)
khenaidoo26721882021-08-11 17:42:52 -0400238 f := t.Field(i)
239 tagField := f.Tag.Get("protobuf")
240 p.Init(f.Type, f.Name, tagField, &f)
Scott Baker2c1c4822019-10-16 11:02:41 -0700241
khenaidoo26721882021-08-11 17:42:52 -0400242 tagOneof := f.Tag.Get("protobuf_oneof")
243 if tagOneof != "" {
244 hasOneof = true
245 p.OrigName = tagOneof
Scott Baker2c1c4822019-10-16 11:02:41 -0700246 }
khenaidoo26721882021-08-11 17:42:52 -0400247
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
Scott Baker2c1c4822019-10-16 11:02:41 -0700272 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700273 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700274
Scott Baker2c1c4822019-10-16 11:02:41 -0700275 prop.OneofTypes = make(map[string]*OneofProperties)
khenaidoo26721882021-08-11 17:42:52 -0400276 for _, wrapper := range oneofWrappers {
277 p := &OneofProperties{
278 Type: reflect.ValueOf(wrapper).Type(), // *T
Scott Baker2c1c4822019-10-16 11:02:41 -0700279 Prop: new(Properties),
280 }
khenaidoo26721882021-08-11 17:42:52 -0400281 f := p.Type.Elem().Field(0)
282 p.Prop.Name = f.Name
283 p.Prop.Parse(f.Tag.Get("protobuf"))
Scott Baker2c1c4822019-10-16 11:02:41 -0700284
khenaidoo26721882021-08-11 17:42:52 -0400285 // 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
Scott Baker2c1c4822019-10-16 11:02:41 -0700298 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700299 }
Scott Baker2c1c4822019-10-16 11:02:41 -0700300
301 return prop
302}
303
khenaidoo26721882021-08-11 17:42:52 -0400304func (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 }