stringer.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. // Package typefmt provides functionality to format descriptors.
  5. package typefmt
  6. import (
  7. "fmt"
  8. "io"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "github.com/golang/protobuf/v2/internal/detrand"
  13. "github.com/golang/protobuf/v2/internal/pragma"
  14. pref "github.com/golang/protobuf/v2/reflect/protoreflect"
  15. )
  16. type list interface {
  17. Len() int
  18. pragma.DoNotImplement
  19. }
  20. func FormatList(s fmt.State, r rune, vs list) {
  21. io.WriteString(s, formatListOpt(vs, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
  22. }
  23. func formatListOpt(vs list, isRoot, allowMulti bool) string {
  24. start, end := "[", "]"
  25. if isRoot {
  26. var name string
  27. switch vs.(type) {
  28. case pref.FieldNumbers:
  29. name = "FieldNumbers"
  30. case pref.FieldRanges:
  31. name = "FieldRanges"
  32. case pref.FileImports:
  33. name = "FileImports"
  34. case pref.Descriptor:
  35. name = reflect.ValueOf(vs).MethodByName("Get").Type().Out(0).Name() + "s"
  36. }
  37. start, end = name+randomSpace()+"{", "}"
  38. }
  39. var ss []string
  40. switch vs := vs.(type) {
  41. case pref.FieldNumbers:
  42. for i := 0; i < vs.Len(); i++ {
  43. ss = append(ss, fmt.Sprint(vs.Get(i)))
  44. }
  45. return start + joinStrings(ss, false) + end
  46. case pref.FieldRanges:
  47. for i := 0; i < vs.Len(); i++ {
  48. r := vs.Get(i)
  49. if r[0]+1 == r[1] {
  50. ss = append(ss, fmt.Sprintf("%d", r[0]))
  51. } else {
  52. ss = append(ss, fmt.Sprintf("%d:%d", r[0], r[1]))
  53. }
  54. }
  55. return start + joinStrings(ss, false) + end
  56. case pref.FileImports:
  57. for i := 0; i < vs.Len(); i++ {
  58. var rs records
  59. rs.Append(reflect.ValueOf(vs.Get(i)), "Path", "Package", "IsPublic", "IsWeak")
  60. ss = append(ss, "{"+rs.Join()+"}")
  61. }
  62. return start + joinStrings(ss, allowMulti) + end
  63. default:
  64. _, isEnumValue := vs.(pref.EnumValueDescriptors)
  65. for i := 0; i < vs.Len(); i++ {
  66. m := reflect.ValueOf(vs).MethodByName("Get")
  67. v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
  68. ss = append(ss, formatDescOpt(v.(pref.Descriptor), false, allowMulti && !isEnumValue))
  69. }
  70. return start + joinStrings(ss, allowMulti && isEnumValue) + end
  71. }
  72. }
  73. // descriptorAccessors is a list of accessors to print for each descriptor.
  74. //
  75. // Do not print all accessors since some contain redundant information,
  76. // while others are pointers that we do not want to follow since the descriptor
  77. // is actually a cyclic graph.
  78. //
  79. // Using a list allows us to print the accessors in a sensible order.
  80. var descriptorAccessors = map[reflect.Type][]string{
  81. reflect.TypeOf((*pref.FileDescriptor)(nil)).Elem(): {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"},
  82. reflect.TypeOf((*pref.MessageDescriptor)(nil)).Elem(): {"IsMapEntry", "Fields", "Oneofs", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"},
  83. reflect.TypeOf((*pref.FieldDescriptor)(nil)).Elem(): {"Number", "Cardinality", "Kind", "JSONName", "IsPacked", "IsMap", "IsWeak", "HasDefault", "Default", "OneofType", "ExtendedType", "MessageType", "EnumType"},
  84. reflect.TypeOf((*pref.OneofDescriptor)(nil)).Elem(): {"Fields"}, // not directly used; must keep in sync with formatDescOpt
  85. reflect.TypeOf((*pref.EnumDescriptor)(nil)).Elem(): {"Values"},
  86. reflect.TypeOf((*pref.EnumValueDescriptor)(nil)).Elem(): {"Number"},
  87. reflect.TypeOf((*pref.ServiceDescriptor)(nil)).Elem(): {"Methods"},
  88. reflect.TypeOf((*pref.MethodDescriptor)(nil)).Elem(): {"InputType", "OutputType", "IsStreamingClient", "IsStreamingServer"},
  89. }
  90. func FormatDesc(s fmt.State, r rune, t pref.Descriptor) {
  91. io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
  92. }
  93. func formatDescOpt(t pref.Descriptor, isRoot, allowMulti bool) string {
  94. rv := reflect.ValueOf(t)
  95. rt := rv.MethodByName("ProtoType").Type().In(0)
  96. start, end := "{", "}"
  97. if isRoot {
  98. start = rt.Name() + randomSpace() + "{"
  99. }
  100. _, isFile := t.(pref.FileDescriptor)
  101. rs := records{allowMulti: allowMulti}
  102. if t.IsPlaceholder() {
  103. if isFile {
  104. rs.Append(rv, "Path", "Package", "IsPlaceholder")
  105. } else {
  106. rs.Append(rv, "FullName", "IsPlaceholder")
  107. }
  108. } else {
  109. switch {
  110. case isFile:
  111. rs.Append(rv, "Syntax")
  112. case isRoot:
  113. rs.Append(rv, "Syntax", "FullName")
  114. default:
  115. rs.Append(rv, "Name")
  116. }
  117. if t, ok := t.(pref.OneofDescriptor); ok {
  118. var ss []string
  119. fs := t.Fields()
  120. for i := 0; i < fs.Len(); i++ {
  121. ss = append(ss, string(fs.Get(i).Name()))
  122. }
  123. if len(ss) > 0 {
  124. rs.recs = append(rs.recs, [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
  125. }
  126. } else {
  127. rs.Append(rv, descriptorAccessors[rt]...)
  128. }
  129. if rv.MethodByName("GoType").IsValid() {
  130. rs.Append(rv, "GoType")
  131. }
  132. }
  133. return start + rs.Join() + end
  134. }
  135. type records struct {
  136. recs [][2]string
  137. allowMulti bool
  138. }
  139. func (rs *records) Append(v reflect.Value, accessors ...string) {
  140. for _, a := range accessors {
  141. var rv reflect.Value
  142. if m := v.MethodByName(a); m.IsValid() {
  143. rv = m.Call(nil)[0]
  144. }
  145. if v.Kind() == reflect.Struct && !rv.IsValid() {
  146. rv = v.FieldByName(a)
  147. }
  148. if !rv.IsValid() {
  149. panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a))
  150. }
  151. if _, ok := rv.Interface().(pref.Value); ok {
  152. rv = rv.MethodByName("Interface").Call(nil)[0]
  153. if !rv.IsNil() {
  154. rv = rv.Elem()
  155. }
  156. }
  157. // Ignore zero values.
  158. var isZero bool
  159. switch rv.Kind() {
  160. case reflect.Interface, reflect.Slice:
  161. isZero = rv.IsNil()
  162. case reflect.Bool:
  163. isZero = rv.Bool() == false
  164. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  165. isZero = rv.Int() == 0
  166. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  167. isZero = rv.Uint() == 0
  168. case reflect.String:
  169. isZero = rv.String() == ""
  170. }
  171. if n, ok := rv.Interface().(list); ok {
  172. isZero = n.Len() == 0
  173. }
  174. if isZero {
  175. continue
  176. }
  177. // Format the value.
  178. var s string
  179. v := rv.Interface()
  180. switch v := v.(type) {
  181. case list:
  182. s = formatListOpt(v, false, rs.allowMulti)
  183. case pref.FieldDescriptor, pref.OneofDescriptor, pref.EnumValueDescriptor, pref.MethodDescriptor:
  184. s = string(v.(pref.Descriptor).Name())
  185. case pref.Descriptor:
  186. s = string(v.FullName())
  187. case string:
  188. s = strconv.Quote(v)
  189. case []byte:
  190. s = fmt.Sprintf("%q", v)
  191. default:
  192. s = fmt.Sprint(v)
  193. }
  194. rs.recs = append(rs.recs, [2]string{a, s})
  195. }
  196. }
  197. func (rs *records) Join() string {
  198. var ss []string
  199. // In single line mode, simply join all records with commas.
  200. if !rs.allowMulti {
  201. for _, r := range rs.recs {
  202. ss = append(ss, r[0]+": "+r[1])
  203. }
  204. return joinStrings(ss, false)
  205. }
  206. // In allowMulti line mode, align single line records for more readable output.
  207. var maxLen int
  208. flush := func(i int) {
  209. for _, r := range rs.recs[len(ss):i] {
  210. padding := strings.Repeat(" ", maxLen-len(r[0]))
  211. ss = append(ss, r[0]+": "+padding+r[1])
  212. }
  213. maxLen = 0
  214. }
  215. for i, r := range rs.recs {
  216. if isMulti := strings.Contains(r[1], "\n"); isMulti {
  217. flush(i)
  218. ss = append(ss, r[0]+": "+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
  219. } else if maxLen < len(r[0]) {
  220. maxLen = len(r[0])
  221. }
  222. }
  223. flush(len(rs.recs))
  224. return joinStrings(ss, true)
  225. }
  226. func joinStrings(ss []string, isMulti bool) string {
  227. if len(ss) == 0 {
  228. return ""
  229. }
  230. if isMulti {
  231. return "\n\t" + strings.Join(ss, "\n\t") + "\n"
  232. }
  233. return strings.Join(ss, ", ")
  234. }
  235. // randomSpace randomly returns a string that is either empty or a single space.
  236. // This is done deliberately to ensure that the output is slightly non-stable.
  237. //
  238. // These makes it harder for people to depend on the debug string as stable
  239. // and provides us the flexibility to make changes.
  240. func randomSpace() string {
  241. return " "[:detrand.Intn(2)]
  242. }