stringer.go 8.2 KB

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