You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

319 lines
9.7 KiB

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