stringer.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 descfmt provides functionality to format descriptors.
  5. package descfmt
  6. import (
  7. "fmt"
  8. "io"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "google.golang.org/protobuf/internal/detrand"
  13. "google.golang.org/protobuf/internal/pragma"
  14. pref "google.golang.org/protobuf/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+"{", "}"
  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", "IsExtension", "IsWeak", "IsList", "IsMap", "MapKey", "MapValue", "HasDefault", "Default", "ContainingOneof", "ContainingMessage", "Message", "Enum"},
  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(): {"Input", "Output", "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() + "{"
  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. switch t := t.(type) {
  137. case pref.FieldDescriptor:
  138. for _, s := range descriptorAccessors[rt] {
  139. switch s {
  140. case "MapKey":
  141. if k := t.MapKey(); k != nil {
  142. rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()})
  143. }
  144. case "MapValue":
  145. if v := t.MapValue(); v != nil {
  146. switch v.Kind() {
  147. case pref.EnumKind:
  148. rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Enum().FullName())})
  149. case pref.MessageKind, pref.GroupKind:
  150. rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Message().FullName())})
  151. default:
  152. rs.recs = append(rs.recs, [2]string{"MapValue", v.Kind().String()})
  153. }
  154. }
  155. case "ContainingOneof":
  156. if od := t.ContainingOneof(); od != nil {
  157. rs.recs = append(rs.recs, [2]string{"Oneof", string(od.Name())})
  158. }
  159. case "ContainingMessage":
  160. if t.IsExtension() {
  161. rs.recs = append(rs.recs, [2]string{"Extendee", string(t.ContainingMessage().FullName())})
  162. }
  163. case "Message":
  164. if !t.IsMap() {
  165. rs.Append(rv, s)
  166. }
  167. default:
  168. rs.Append(rv, s)
  169. }
  170. }
  171. case pref.OneofDescriptor:
  172. var ss []string
  173. fs := t.Fields()
  174. for i := 0; i < fs.Len(); i++ {
  175. ss = append(ss, string(fs.Get(i).Name()))
  176. }
  177. if len(ss) > 0 {
  178. rs.recs = append(rs.recs, [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
  179. }
  180. default:
  181. rs.Append(rv, descriptorAccessors[rt]...)
  182. }
  183. if rv.MethodByName("GoType").IsValid() {
  184. rs.Append(rv, "GoType")
  185. }
  186. }
  187. return start + rs.Join() + end
  188. }
  189. type records struct {
  190. recs [][2]string
  191. allowMulti bool
  192. }
  193. func (rs *records) Append(v reflect.Value, accessors ...string) {
  194. for _, a := range accessors {
  195. var rv reflect.Value
  196. if m := v.MethodByName(a); m.IsValid() {
  197. rv = m.Call(nil)[0]
  198. }
  199. if v.Kind() == reflect.Struct && !rv.IsValid() {
  200. rv = v.FieldByName(a)
  201. }
  202. if !rv.IsValid() {
  203. panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a))
  204. }
  205. if _, ok := rv.Interface().(pref.Value); ok {
  206. rv = rv.MethodByName("Interface").Call(nil)[0]
  207. if !rv.IsNil() {
  208. rv = rv.Elem()
  209. }
  210. }
  211. // Ignore zero values.
  212. var isZero bool
  213. switch rv.Kind() {
  214. case reflect.Interface, reflect.Slice:
  215. isZero = rv.IsNil()
  216. case reflect.Bool:
  217. isZero = rv.Bool() == false
  218. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  219. isZero = rv.Int() == 0
  220. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  221. isZero = rv.Uint() == 0
  222. case reflect.String:
  223. isZero = rv.String() == ""
  224. }
  225. if n, ok := rv.Interface().(list); ok {
  226. isZero = n.Len() == 0
  227. }
  228. if isZero {
  229. continue
  230. }
  231. // Format the value.
  232. var s string
  233. v := rv.Interface()
  234. switch v := v.(type) {
  235. case list:
  236. s = formatListOpt(v, false, rs.allowMulti)
  237. case pref.FieldDescriptor, pref.OneofDescriptor, pref.EnumValueDescriptor, pref.MethodDescriptor:
  238. s = string(v.(pref.Descriptor).Name())
  239. case pref.Descriptor:
  240. s = string(v.FullName())
  241. case string:
  242. s = strconv.Quote(v)
  243. case []byte:
  244. s = fmt.Sprintf("%q", v)
  245. default:
  246. s = fmt.Sprint(v)
  247. }
  248. rs.recs = append(rs.recs, [2]string{a, s})
  249. }
  250. }
  251. func (rs *records) Join() string {
  252. var ss []string
  253. // In single line mode, simply join all records with commas.
  254. if !rs.allowMulti {
  255. for _, r := range rs.recs {
  256. ss = append(ss, r[0]+formatColon(0)+r[1])
  257. }
  258. return joinStrings(ss, false)
  259. }
  260. // In allowMulti line mode, align single line records for more readable output.
  261. var maxLen int
  262. flush := func(i int) {
  263. for _, r := range rs.recs[len(ss):i] {
  264. ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
  265. }
  266. maxLen = 0
  267. }
  268. for i, r := range rs.recs {
  269. if isMulti := strings.Contains(r[1], "\n"); isMulti {
  270. flush(i)
  271. ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
  272. } else if maxLen < len(r[0]) {
  273. maxLen = len(r[0])
  274. }
  275. }
  276. flush(len(rs.recs))
  277. return joinStrings(ss, true)
  278. }
  279. func formatColon(padding int) string {
  280. // Deliberately introduce instability into the debug output to
  281. // discourage users from performing string comparisons.
  282. // This provides us flexibility to change the output in the future.
  283. if detrand.Bool() {
  284. return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0)
  285. } else {
  286. return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020)
  287. }
  288. }
  289. func joinStrings(ss []string, isMulti bool) string {
  290. if len(ss) == 0 {
  291. return ""
  292. }
  293. if isMulti {
  294. return "\n\t" + strings.Join(ss, "\n\t") + "\n"
  295. }
  296. return strings.Join(ss, ", ")
  297. }