khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 1 | // Copyright 2018 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 | |
| 5 | // Package descfmt provides functionality to format descriptors. |
| 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" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 17 | "google.golang.org/protobuf/reflect/protoreflect" |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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) { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 33 | case protoreflect.Names: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 34 | name = "Names" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 35 | case protoreflect.FieldNumbers: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 36 | name = "FieldNumbers" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 37 | case protoreflect.FieldRanges: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 38 | name = "FieldRanges" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 39 | case protoreflect.EnumRanges: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 40 | name = "EnumRanges" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 41 | case protoreflect.FileImports: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 42 | name = "FileImports" |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 43 | case protoreflect.Descriptor: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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) { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 53 | case protoreflect.Names: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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 |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 58 | case protoreflect.FieldNumbers: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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 |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 63 | case protoreflect.FieldRanges: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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])) // enum ranges are end exclusive |
| 70 | } |
| 71 | } |
| 72 | return start + joinStrings(ss, false) + end |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 73 | case protoreflect.EnumRanges: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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)) // enum ranges are end inclusive |
| 80 | } |
| 81 | } |
| 82 | return start + joinStrings(ss, false) + end |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 83 | case protoreflect.FileImports: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 84 | for i := 0; i < vs.Len(); i++ { |
| 85 | var rs records |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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 | }...) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 93 | ss = append(ss, "{"+rs.Join()+"}") |
| 94 | } |
| 95 | return start + joinStrings(ss, allowMulti) + end |
| 96 | default: |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 97 | _, isEnumValue := vs.(protoreflect.EnumValueDescriptors) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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() |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 101 | ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue, nil)) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 102 | } |
| 103 | return start + joinStrings(ss, allowMulti && isEnumValue) + end |
| 104 | } |
| 105 | } |
| 106 | |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 107 | type methodAndName struct { |
| 108 | method reflect.Value |
| 109 | name string |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 110 | } |
| 111 | |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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)) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 114 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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 { |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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 | |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 129 | _, isFile := t.(protoreflect.FileDescriptor) |
| 130 | rs := records{ |
| 131 | allowMulti: allowMulti, |
| 132 | record: record, |
| 133 | } |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 134 | if t.IsPlaceholder() { |
| 135 | if isFile { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 136 | rs.Append(rv, []methodAndName{ |
| 137 | {rv.MethodByName("Path"), "Path"}, |
| 138 | {rv.MethodByName("Package"), "Package"}, |
| 139 | {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"}, |
| 140 | }...) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 141 | } else { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 142 | rs.Append(rv, []methodAndName{ |
| 143 | {rv.MethodByName("FullName"), "FullName"}, |
| 144 | {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"}, |
| 145 | }...) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 146 | } |
| 147 | } else { |
| 148 | switch { |
| 149 | case isFile: |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 150 | rs.Append(rv, methodAndName{rv.MethodByName("Syntax"), "Syntax"}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 151 | case isRoot: |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 152 | rs.Append(rv, []methodAndName{ |
| 153 | {rv.MethodByName("Syntax"), "Syntax"}, |
| 154 | {rv.MethodByName("FullName"), "FullName"}, |
| 155 | }...) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 156 | default: |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 157 | rs.Append(rv, methodAndName{rv.MethodByName("Name"), "Name"}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 158 | } |
| 159 | switch t := t.(type) { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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 { |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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() { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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())}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 195 | default: |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 196 | rs.AppendRecs("MapValue", [2]string{"MapValue", v.Kind().String()}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 197 | } |
| 198 | } |
| 199 | case "ContainingOneof": |
| 200 | if od := t.ContainingOneof(); od != nil { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 201 | rs.AppendRecs("ContainingOneof", [2]string{"Oneof", string(od.Name())}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 202 | } |
| 203 | case "ContainingMessage": |
| 204 | if t.IsExtension() { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 205 | rs.AppendRecs("ContainingMessage", [2]string{"Extendee", string(t.ContainingMessage().FullName())}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 206 | } |
| 207 | case "Message": |
| 208 | if !t.IsMap() { |
| 209 | rs.Append(rv, s) |
| 210 | } |
| 211 | default: |
| 212 | rs.Append(rv, s) |
| 213 | } |
| 214 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 215 | case protoreflect.OneofDescriptor: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 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 { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 222 | rs.AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(ss, false) + "]"}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 223 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 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 | {rv.MethodByName("IsClosed"), "IsClosed"}, |
| 256 | }...) |
| 257 | |
| 258 | case protoreflect.EnumValueDescriptor: |
| 259 | rs.Append(rv, []methodAndName{ |
| 260 | {rv.MethodByName("Number"), "Number"}, |
| 261 | }...) |
| 262 | |
| 263 | case protoreflect.ServiceDescriptor: |
| 264 | rs.Append(rv, []methodAndName{ |
| 265 | {rv.MethodByName("Methods"), "Methods"}, |
| 266 | }...) |
| 267 | |
| 268 | case protoreflect.MethodDescriptor: |
| 269 | rs.Append(rv, []methodAndName{ |
| 270 | {rv.MethodByName("Input"), "Input"}, |
| 271 | {rv.MethodByName("Output"), "Output"}, |
| 272 | {rv.MethodByName("IsStreamingClient"), "IsStreamingClient"}, |
| 273 | {rv.MethodByName("IsStreamingServer"), "IsStreamingServer"}, |
| 274 | }...) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 275 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 276 | if m := rv.MethodByName("GoType"); m.IsValid() { |
| 277 | rs.Append(rv, methodAndName{m, "GoType"}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 278 | } |
| 279 | } |
| 280 | return start + rs.Join() + end |
| 281 | } |
| 282 | |
| 283 | type records struct { |
| 284 | recs [][2]string |
| 285 | allowMulti bool |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 286 | |
| 287 | // record is a function that will be called for every Append() or |
| 288 | // AppendRecs() call, to be used for testing with the |
| 289 | // InternalFormatDescOptForTesting function. |
| 290 | record func(string) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 291 | } |
| 292 | |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 293 | func (rs *records) AppendRecs(fieldName string, newRecs [2]string) { |
| 294 | if rs.record != nil { |
| 295 | rs.record(fieldName) |
| 296 | } |
| 297 | rs.recs = append(rs.recs, newRecs) |
| 298 | } |
| 299 | |
| 300 | func (rs *records) Append(v reflect.Value, accessors ...methodAndName) { |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 301 | for _, a := range accessors { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 302 | if rs.record != nil { |
| 303 | rs.record(a.name) |
| 304 | } |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 305 | var rv reflect.Value |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 306 | if a.method.IsValid() { |
| 307 | rv = a.method.Call(nil)[0] |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 308 | } |
| 309 | if v.Kind() == reflect.Struct && !rv.IsValid() { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 310 | rv = v.FieldByName(a.name) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 311 | } |
| 312 | if !rv.IsValid() { |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 313 | panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name)) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 314 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 315 | if _, ok := rv.Interface().(protoreflect.Value); ok { |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 316 | rv = rv.MethodByName("Interface").Call(nil)[0] |
| 317 | if !rv.IsNil() { |
| 318 | rv = rv.Elem() |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | // Ignore zero values. |
| 323 | var isZero bool |
| 324 | switch rv.Kind() { |
| 325 | case reflect.Interface, reflect.Slice: |
| 326 | isZero = rv.IsNil() |
| 327 | case reflect.Bool: |
| 328 | isZero = rv.Bool() == false |
| 329 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| 330 | isZero = rv.Int() == 0 |
| 331 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| 332 | isZero = rv.Uint() == 0 |
| 333 | case reflect.String: |
| 334 | isZero = rv.String() == "" |
| 335 | } |
| 336 | if n, ok := rv.Interface().(list); ok { |
| 337 | isZero = n.Len() == 0 |
| 338 | } |
| 339 | if isZero { |
| 340 | continue |
| 341 | } |
| 342 | |
| 343 | // Format the value. |
| 344 | var s string |
| 345 | v := rv.Interface() |
| 346 | switch v := v.(type) { |
| 347 | case list: |
| 348 | s = formatListOpt(v, false, rs.allowMulti) |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 349 | case protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor: |
| 350 | s = string(v.(protoreflect.Descriptor).Name()) |
| 351 | case protoreflect.Descriptor: |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 352 | s = string(v.FullName()) |
| 353 | case string: |
| 354 | s = strconv.Quote(v) |
| 355 | case []byte: |
| 356 | s = fmt.Sprintf("%q", v) |
| 357 | default: |
| 358 | s = fmt.Sprint(v) |
| 359 | } |
Akash Reddy Kankanala | 92dfdf8 | 2025-03-23 22:07:09 +0530 | [diff] [blame^] | 360 | rs.recs = append(rs.recs, [2]string{a.name, s}) |
khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 361 | } |
| 362 | } |
| 363 | |
| 364 | func (rs *records) Join() string { |
| 365 | var ss []string |
| 366 | |
| 367 | // In single line mode, simply join all records with commas. |
| 368 | if !rs.allowMulti { |
| 369 | for _, r := range rs.recs { |
| 370 | ss = append(ss, r[0]+formatColon(0)+r[1]) |
| 371 | } |
| 372 | return joinStrings(ss, false) |
| 373 | } |
| 374 | |
| 375 | // In allowMulti line mode, align single line records for more readable output. |
| 376 | var maxLen int |
| 377 | flush := func(i int) { |
| 378 | for _, r := range rs.recs[len(ss):i] { |
| 379 | ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1]) |
| 380 | } |
| 381 | maxLen = 0 |
| 382 | } |
| 383 | for i, r := range rs.recs { |
| 384 | if isMulti := strings.Contains(r[1], "\n"); isMulti { |
| 385 | flush(i) |
| 386 | ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t")) |
| 387 | } else if maxLen < len(r[0]) { |
| 388 | maxLen = len(r[0]) |
| 389 | } |
| 390 | } |
| 391 | flush(len(rs.recs)) |
| 392 | return joinStrings(ss, true) |
| 393 | } |
| 394 | |
| 395 | func formatColon(padding int) string { |
| 396 | // Deliberately introduce instability into the debug output to |
| 397 | // discourage users from performing string comparisons. |
| 398 | // This provides us flexibility to change the output in the future. |
| 399 | if detrand.Bool() { |
| 400 | return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0) |
| 401 | } else { |
| 402 | return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020) |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | func joinStrings(ss []string, isMulti bool) string { |
| 407 | if len(ss) == 0 { |
| 408 | return "" |
| 409 | } |
| 410 | if isMulti { |
| 411 | return "\n\t" + strings.Join(ss, "\n\t") + "\n" |
| 412 | } |
| 413 | return strings.Join(ss, ", ") |
| 414 | } |