stringer.go 7.5 KB

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