1
2
3
4
5
6 package descfmt
7
8 import (
9 "fmt"
10 "io"
11 "reflect"
12 "strconv"
13 "strings"
14
15 "google.golang.org/protobuf/internal/detrand"
16 "google.golang.org/protobuf/internal/pragma"
17 "google.golang.org/protobuf/reflect/protoreflect"
18 )
19
20 type list interface {
21 Len() int
22 pragma.DoNotImplement
23 }
24
25 func FormatList(s fmt.State, r rune, vs list) {
26 io.WriteString(s, formatListOpt(vs, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
27 }
28 func formatListOpt(vs list, isRoot, allowMulti bool) string {
29 start, end := "[", "]"
30 if isRoot {
31 var name string
32 switch vs.(type) {
33 case protoreflect.Names:
34 name = "Names"
35 case protoreflect.FieldNumbers:
36 name = "FieldNumbers"
37 case protoreflect.FieldRanges:
38 name = "FieldRanges"
39 case protoreflect.EnumRanges:
40 name = "EnumRanges"
41 case protoreflect.FileImports:
42 name = "FileImports"
43 case protoreflect.Descriptor:
44 name = reflect.ValueOf(vs).MethodByName("Get").Type().Out(0).Name() + "s"
45 default:
46 name = reflect.ValueOf(vs).Elem().Type().Name()
47 }
48 start, end = name+"{", "}"
49 }
50
51 var ss []string
52 switch vs := vs.(type) {
53 case protoreflect.Names:
54 for i := 0; i < vs.Len(); i++ {
55 ss = append(ss, fmt.Sprint(vs.Get(i)))
56 }
57 return start + joinStrings(ss, false) + end
58 case protoreflect.FieldNumbers:
59 for i := 0; i < vs.Len(); i++ {
60 ss = append(ss, fmt.Sprint(vs.Get(i)))
61 }
62 return start + joinStrings(ss, false) + end
63 case protoreflect.FieldRanges:
64 for i := 0; i < vs.Len(); i++ {
65 r := vs.Get(i)
66 if r[0]+1 == r[1] {
67 ss = append(ss, fmt.Sprintf("%d", r[0]))
68 } else {
69 ss = append(ss, fmt.Sprintf("%d:%d", r[0], r[1]))
70 }
71 }
72 return start + joinStrings(ss, false) + end
73 case protoreflect.EnumRanges:
74 for i := 0; i < vs.Len(); i++ {
75 r := vs.Get(i)
76 if r[0] == r[1] {
77 ss = append(ss, fmt.Sprintf("%d", r[0]))
78 } else {
79 ss = append(ss, fmt.Sprintf("%d:%d", r[0], int64(r[1])+1))
80 }
81 }
82 return start + joinStrings(ss, false) + end
83 case protoreflect.FileImports:
84 for i := 0; i < vs.Len(); i++ {
85 var rs records
86 rv := reflect.ValueOf(vs.Get(i))
87 rs.Append(rv, []methodAndName{
88 {rv.MethodByName("Path"), "Path"},
89 {rv.MethodByName("Package"), "Package"},
90 {rv.MethodByName("IsPublic"), "IsPublic"},
91 {rv.MethodByName("IsWeak"), "IsWeak"},
92 }...)
93 ss = append(ss, "{"+rs.Join()+"}")
94 }
95 return start + joinStrings(ss, allowMulti) + end
96 default:
97 _, isEnumValue := vs.(protoreflect.EnumValueDescriptors)
98 for i := 0; i < vs.Len(); i++ {
99 m := reflect.ValueOf(vs).MethodByName("Get")
100 v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
101 ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue, nil))
102 }
103 return start + joinStrings(ss, allowMulti && isEnumValue) + end
104 }
105 }
106
107 type methodAndName struct {
108 method reflect.Value
109 name string
110 }
111
112 func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) {
113 io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')), nil))
114 }
115
116 func InternalFormatDescOptForTesting(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
117 return formatDescOpt(t, isRoot, allowMulti, record)
118 }
119
120 func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
121 rv := reflect.ValueOf(t)
122 rt := rv.MethodByName("ProtoType").Type().In(0)
123
124 start, end := "{", "}"
125 if isRoot {
126 start = rt.Name() + "{"
127 }
128
129 _, isFile := t.(protoreflect.FileDescriptor)
130 rs := records{
131 allowMulti: allowMulti,
132 record: record,
133 }
134 if t.IsPlaceholder() {
135 if isFile {
136 rs.Append(rv, []methodAndName{
137 {rv.MethodByName("Path"), "Path"},
138 {rv.MethodByName("Package"), "Package"},
139 {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
140 }...)
141 } else {
142 rs.Append(rv, []methodAndName{
143 {rv.MethodByName("FullName"), "FullName"},
144 {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
145 }...)
146 }
147 } else {
148 switch {
149 case isFile:
150 rs.Append(rv, methodAndName{rv.MethodByName("Syntax"), "Syntax"})
151 case isRoot:
152 rs.Append(rv, []methodAndName{
153 {rv.MethodByName("Syntax"), "Syntax"},
154 {rv.MethodByName("FullName"), "FullName"},
155 }...)
156 default:
157 rs.Append(rv, methodAndName{rv.MethodByName("Name"), "Name"})
158 }
159 switch t := t.(type) {
160 case protoreflect.FieldDescriptor:
161 accessors := []methodAndName{
162 {rv.MethodByName("Number"), "Number"},
163 {rv.MethodByName("Cardinality"), "Cardinality"},
164 {rv.MethodByName("Kind"), "Kind"},
165 {rv.MethodByName("HasJSONName"), "HasJSONName"},
166 {rv.MethodByName("JSONName"), "JSONName"},
167 {rv.MethodByName("HasPresence"), "HasPresence"},
168 {rv.MethodByName("IsExtension"), "IsExtension"},
169 {rv.MethodByName("IsPacked"), "IsPacked"},
170 {rv.MethodByName("IsWeak"), "IsWeak"},
171 {rv.MethodByName("IsList"), "IsList"},
172 {rv.MethodByName("IsMap"), "IsMap"},
173 {rv.MethodByName("MapKey"), "MapKey"},
174 {rv.MethodByName("MapValue"), "MapValue"},
175 {rv.MethodByName("HasDefault"), "HasDefault"},
176 {rv.MethodByName("Default"), "Default"},
177 {rv.MethodByName("ContainingOneof"), "ContainingOneof"},
178 {rv.MethodByName("ContainingMessage"), "ContainingMessage"},
179 {rv.MethodByName("Message"), "Message"},
180 {rv.MethodByName("Enum"), "Enum"},
181 }
182 for _, s := range accessors {
183 switch s.name {
184 case "MapKey":
185 if k := t.MapKey(); k != nil {
186 rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()})
187 }
188 case "MapValue":
189 if v := t.MapValue(); v != nil {
190 switch v.Kind() {
191 case protoreflect.EnumKind:
192 rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Enum().FullName())})
193 case protoreflect.MessageKind, protoreflect.GroupKind:
194 rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Message().FullName())})
195 default:
196 rs.AppendRecs("MapValue", [2]string{"MapValue", v.Kind().String()})
197 }
198 }
199 case "ContainingOneof":
200 if od := t.ContainingOneof(); od != nil {
201 rs.AppendRecs("ContainingOneof", [2]string{"Oneof", string(od.Name())})
202 }
203 case "ContainingMessage":
204 if t.IsExtension() {
205 rs.AppendRecs("ContainingMessage", [2]string{"Extendee", string(t.ContainingMessage().FullName())})
206 }
207 case "Message":
208 if !t.IsMap() {
209 rs.Append(rv, s)
210 }
211 default:
212 rs.Append(rv, s)
213 }
214 }
215 case protoreflect.OneofDescriptor:
216 var ss []string
217 fs := t.Fields()
218 for i := 0; i < fs.Len(); i++ {
219 ss = append(ss, string(fs.Get(i).Name()))
220 }
221 if len(ss) > 0 {
222 rs.AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
223 }
224
225 case protoreflect.FileDescriptor:
226 rs.Append(rv, []methodAndName{
227 {rv.MethodByName("Path"), "Path"},
228 {rv.MethodByName("Package"), "Package"},
229 {rv.MethodByName("Imports"), "Imports"},
230 {rv.MethodByName("Messages"), "Messages"},
231 {rv.MethodByName("Enums"), "Enums"},
232 {rv.MethodByName("Extensions"), "Extensions"},
233 {rv.MethodByName("Services"), "Services"},
234 }...)
235
236 case protoreflect.MessageDescriptor:
237 rs.Append(rv, []methodAndName{
238 {rv.MethodByName("IsMapEntry"), "IsMapEntry"},
239 {rv.MethodByName("Fields"), "Fields"},
240 {rv.MethodByName("Oneofs"), "Oneofs"},
241 {rv.MethodByName("ReservedNames"), "ReservedNames"},
242 {rv.MethodByName("ReservedRanges"), "ReservedRanges"},
243 {rv.MethodByName("RequiredNumbers"), "RequiredNumbers"},
244 {rv.MethodByName("ExtensionRanges"), "ExtensionRanges"},
245 {rv.MethodByName("Messages"), "Messages"},
246 {rv.MethodByName("Enums"), "Enums"},
247 {rv.MethodByName("Extensions"), "Extensions"},
248 }...)
249
250 case protoreflect.EnumDescriptor:
251 rs.Append(rv, []methodAndName{
252 {rv.MethodByName("Values"), "Values"},
253 {rv.MethodByName("ReservedNames"), "ReservedNames"},
254 {rv.MethodByName("ReservedRanges"), "ReservedRanges"},
255 }...)
256
257 case protoreflect.EnumValueDescriptor:
258 rs.Append(rv, []methodAndName{
259 {rv.MethodByName("Number"), "Number"},
260 }...)
261
262 case protoreflect.ServiceDescriptor:
263 rs.Append(rv, []methodAndName{
264 {rv.MethodByName("Methods"), "Methods"},
265 }...)
266
267 case protoreflect.MethodDescriptor:
268 rs.Append(rv, []methodAndName{
269 {rv.MethodByName("Input"), "Input"},
270 {rv.MethodByName("Output"), "Output"},
271 {rv.MethodByName("IsStreamingClient"), "IsStreamingClient"},
272 {rv.MethodByName("IsStreamingServer"), "IsStreamingServer"},
273 }...)
274 }
275 if m := rv.MethodByName("GoType"); m.IsValid() {
276 rs.Append(rv, methodAndName{m, "GoType"})
277 }
278 }
279 return start + rs.Join() + end
280 }
281
282 type records struct {
283 recs [][2]string
284 allowMulti bool
285
286
287
288
289 record func(string)
290 }
291
292 func (rs *records) AppendRecs(fieldName string, newRecs [2]string) {
293 if rs.record != nil {
294 rs.record(fieldName)
295 }
296 rs.recs = append(rs.recs, newRecs)
297 }
298
299 func (rs *records) Append(v reflect.Value, accessors ...methodAndName) {
300 for _, a := range accessors {
301 if rs.record != nil {
302 rs.record(a.name)
303 }
304 var rv reflect.Value
305 if a.method.IsValid() {
306 rv = a.method.Call(nil)[0]
307 }
308 if v.Kind() == reflect.Struct && !rv.IsValid() {
309 rv = v.FieldByName(a.name)
310 }
311 if !rv.IsValid() {
312 panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name))
313 }
314 if _, ok := rv.Interface().(protoreflect.Value); ok {
315 rv = rv.MethodByName("Interface").Call(nil)[0]
316 if !rv.IsNil() {
317 rv = rv.Elem()
318 }
319 }
320
321
322 var isZero bool
323 switch rv.Kind() {
324 case reflect.Interface, reflect.Slice:
325 isZero = rv.IsNil()
326 case reflect.Bool:
327 isZero = rv.Bool() == false
328 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
329 isZero = rv.Int() == 0
330 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
331 isZero = rv.Uint() == 0
332 case reflect.String:
333 isZero = rv.String() == ""
334 }
335 if n, ok := rv.Interface().(list); ok {
336 isZero = n.Len() == 0
337 }
338 if isZero {
339 continue
340 }
341
342
343 var s string
344 v := rv.Interface()
345 switch v := v.(type) {
346 case list:
347 s = formatListOpt(v, false, rs.allowMulti)
348 case protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor:
349 s = string(v.(protoreflect.Descriptor).Name())
350 case protoreflect.Descriptor:
351 s = string(v.FullName())
352 case string:
353 s = strconv.Quote(v)
354 case []byte:
355 s = fmt.Sprintf("%q", v)
356 default:
357 s = fmt.Sprint(v)
358 }
359 rs.recs = append(rs.recs, [2]string{a.name, s})
360 }
361 }
362
363 func (rs *records) Join() string {
364 var ss []string
365
366
367 if !rs.allowMulti {
368 for _, r := range rs.recs {
369 ss = append(ss, r[0]+formatColon(0)+r[1])
370 }
371 return joinStrings(ss, false)
372 }
373
374
375 var maxLen int
376 flush := func(i int) {
377 for _, r := range rs.recs[len(ss):i] {
378 ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
379 }
380 maxLen = 0
381 }
382 for i, r := range rs.recs {
383 if isMulti := strings.Contains(r[1], "\n"); isMulti {
384 flush(i)
385 ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
386 } else if maxLen < len(r[0]) {
387 maxLen = len(r[0])
388 }
389 }
390 flush(len(rs.recs))
391 return joinStrings(ss, true)
392 }
393
394 func formatColon(padding int) string {
395
396
397
398 if detrand.Bool() {
399 return ":" + strings.Repeat(" ", 1+padding)
400 } else {
401 return ":" + strings.Repeat(" ", 1+padding)
402 }
403 }
404
405 func joinStrings(ss []string, isMulti bool) string {
406 if len(ss) == 0 {
407 return ""
408 }
409 if isMulti {
410 return "\n\t" + strings.Join(ss, "\n\t") + "\n"
411 }
412 return strings.Join(ss, ", ")
413 }
414
View as plain text