stringer.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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. rv := reflect.ValueOf(vs.Get(i))
  81. rs.Append(rv, []methodAndName{
  82. {rv.MethodByName("Path"), "Path"},
  83. {rv.MethodByName("Package"), "Package"},
  84. {rv.MethodByName("IsPublic"), "IsPublic"},
  85. {rv.MethodByName("IsWeak"), "IsWeak"},
  86. }...)
  87. ss = append(ss, "{"+rs.Join()+"}")
  88. }
  89. return start + joinStrings(ss, allowMulti) + end
  90. default:
  91. _, isEnumValue := vs.(protoreflect.EnumValueDescriptors)
  92. for i := 0; i < vs.Len(); i++ {
  93. m := reflect.ValueOf(vs).MethodByName("Get")
  94. v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
  95. ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue, nil))
  96. }
  97. return start + joinStrings(ss, allowMulti && isEnumValue) + end
  98. }
  99. }
  100. type methodAndName struct {
  101. method reflect.Value
  102. name string
  103. }
  104. func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) {
  105. io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')), nil))
  106. }
  107. func InternalFormatDescOptForTesting(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
  108. return formatDescOpt(t, isRoot, allowMulti, record)
  109. }
  110. func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
  111. rv := reflect.ValueOf(t)
  112. rt := rv.MethodByName("ProtoType").Type().In(0)
  113. start, end := "{", "}"
  114. if isRoot {
  115. start = rt.Name() + "{"
  116. }
  117. _, isFile := t.(protoreflect.FileDescriptor)
  118. rs := records{
  119. allowMulti: allowMulti,
  120. record: record,
  121. }
  122. if t.IsPlaceholder() {
  123. if isFile {
  124. rs.Append(rv, []methodAndName{
  125. {rv.MethodByName("Path"), "Path"},
  126. {rv.MethodByName("Package"), "Package"},
  127. {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
  128. }...)
  129. } else {
  130. rs.Append(rv, []methodAndName{
  131. {rv.MethodByName("FullName"), "FullName"},
  132. {rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
  133. }...)
  134. }
  135. } else {
  136. switch {
  137. case isFile:
  138. rs.Append(rv, methodAndName{rv.MethodByName("Syntax"), "Syntax"})
  139. case isRoot:
  140. rs.Append(rv, []methodAndName{
  141. {rv.MethodByName("Syntax"), "Syntax"},
  142. {rv.MethodByName("FullName"), "FullName"},
  143. }...)
  144. default:
  145. rs.Append(rv, methodAndName{rv.MethodByName("Name"), "Name"})
  146. }
  147. switch t := t.(type) {
  148. case protoreflect.FieldDescriptor:
  149. accessors := []methodAndName{
  150. {rv.MethodByName("Number"), "Number"},
  151. {rv.MethodByName("Cardinality"), "Cardinality"},
  152. {rv.MethodByName("Kind"), "Kind"},
  153. {rv.MethodByName("HasJSONName"), "HasJSONName"},
  154. {rv.MethodByName("JSONName"), "JSONName"},
  155. {rv.MethodByName("HasPresence"), "HasPresence"},
  156. {rv.MethodByName("IsExtension"), "IsExtension"},
  157. {rv.MethodByName("IsPacked"), "IsPacked"},
  158. {rv.MethodByName("IsWeak"), "IsWeak"},
  159. {rv.MethodByName("IsList"), "IsList"},
  160. {rv.MethodByName("IsMap"), "IsMap"},
  161. {rv.MethodByName("MapKey"), "MapKey"},
  162. {rv.MethodByName("MapValue"), "MapValue"},
  163. {rv.MethodByName("HasDefault"), "HasDefault"},
  164. {rv.MethodByName("Default"), "Default"},
  165. {rv.MethodByName("ContainingOneof"), "ContainingOneof"},
  166. {rv.MethodByName("ContainingMessage"), "ContainingMessage"},
  167. {rv.MethodByName("Message"), "Message"},
  168. {rv.MethodByName("Enum"), "Enum"},
  169. }
  170. for _, s := range accessors {
  171. switch s.name {
  172. case "MapKey":
  173. if k := t.MapKey(); k != nil {
  174. rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()})
  175. }
  176. case "MapValue":
  177. if v := t.MapValue(); v != nil {
  178. switch v.Kind() {
  179. case protoreflect.EnumKind:
  180. rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Enum().FullName())})
  181. case protoreflect.MessageKind, protoreflect.GroupKind:
  182. rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Message().FullName())})
  183. default:
  184. rs.AppendRecs("MapValue", [2]string{"MapValue", v.Kind().String()})
  185. }
  186. }
  187. case "ContainingOneof":
  188. if od := t.ContainingOneof(); od != nil {
  189. rs.AppendRecs("ContainingOneof", [2]string{"Oneof", string(od.Name())})
  190. }
  191. case "ContainingMessage":
  192. if t.IsExtension() {
  193. rs.AppendRecs("ContainingMessage", [2]string{"Extendee", string(t.ContainingMessage().FullName())})
  194. }
  195. case "Message":
  196. if !t.IsMap() {
  197. rs.Append(rv, s)
  198. }
  199. default:
  200. rs.Append(rv, s)
  201. }
  202. }
  203. case protoreflect.OneofDescriptor:
  204. var ss []string
  205. fs := t.Fields()
  206. for i := 0; i < fs.Len(); i++ {
  207. ss = append(ss, string(fs.Get(i).Name()))
  208. }
  209. if len(ss) > 0 {
  210. rs.AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
  211. }
  212. case protoreflect.FileDescriptor:
  213. rs.Append(rv, []methodAndName{
  214. {rv.MethodByName("Path"), "Path"},
  215. {rv.MethodByName("Package"), "Package"},
  216. {rv.MethodByName("Imports"), "Imports"},
  217. {rv.MethodByName("Messages"), "Messages"},
  218. {rv.MethodByName("Enums"), "Enums"},
  219. {rv.MethodByName("Extensions"), "Extensions"},
  220. {rv.MethodByName("Services"), "Services"},
  221. }...)
  222. case protoreflect.MessageDescriptor:
  223. rs.Append(rv, []methodAndName{
  224. {rv.MethodByName("IsMapEntry"), "IsMapEntry"},
  225. {rv.MethodByName("Fields"), "Fields"},
  226. {rv.MethodByName("Oneofs"), "Oneofs"},
  227. {rv.MethodByName("ReservedNames"), "ReservedNames"},
  228. {rv.MethodByName("ReservedRanges"), "ReservedRanges"},
  229. {rv.MethodByName("RequiredNumbers"), "RequiredNumbers"},
  230. {rv.MethodByName("ExtensionRanges"), "ExtensionRanges"},
  231. {rv.MethodByName("Messages"), "Messages"},
  232. {rv.MethodByName("Enums"), "Enums"},
  233. {rv.MethodByName("Extensions"), "Extensions"},
  234. }...)
  235. case protoreflect.EnumDescriptor:
  236. rs.Append(rv, []methodAndName{
  237. {rv.MethodByName("Values"), "Values"},
  238. {rv.MethodByName("ReservedNames"), "ReservedNames"},
  239. {rv.MethodByName("ReservedRanges"), "ReservedRanges"},
  240. {rv.MethodByName("IsClosed"), "IsClosed"},
  241. }...)
  242. case protoreflect.EnumValueDescriptor:
  243. rs.Append(rv, []methodAndName{
  244. {rv.MethodByName("Number"), "Number"},
  245. }...)
  246. case protoreflect.ServiceDescriptor:
  247. rs.Append(rv, []methodAndName{
  248. {rv.MethodByName("Methods"), "Methods"},
  249. }...)
  250. case protoreflect.MethodDescriptor:
  251. rs.Append(rv, []methodAndName{
  252. {rv.MethodByName("Input"), "Input"},
  253. {rv.MethodByName("Output"), "Output"},
  254. {rv.MethodByName("IsStreamingClient"), "IsStreamingClient"},
  255. {rv.MethodByName("IsStreamingServer"), "IsStreamingServer"},
  256. }...)
  257. }
  258. if m := rv.MethodByName("GoType"); m.IsValid() {
  259. rs.Append(rv, methodAndName{m, "GoType"})
  260. }
  261. }
  262. return start + rs.Join() + end
  263. }
  264. type records struct {
  265. recs [][2]string
  266. allowMulti bool
  267. // record is a function that will be called for every Append() or
  268. // AppendRecs() call, to be used for testing with the
  269. // InternalFormatDescOptForTesting function.
  270. record func(string)
  271. }
  272. func (rs *records) AppendRecs(fieldName string, newRecs [2]string) {
  273. if rs.record != nil {
  274. rs.record(fieldName)
  275. }
  276. rs.recs = append(rs.recs, newRecs)
  277. }
  278. func (rs *records) Append(v reflect.Value, accessors ...methodAndName) {
  279. for _, a := range accessors {
  280. if rs.record != nil {
  281. rs.record(a.name)
  282. }
  283. var rv reflect.Value
  284. if a.method.IsValid() {
  285. rv = a.method.Call(nil)[0]
  286. }
  287. if v.Kind() == reflect.Struct && !rv.IsValid() {
  288. rv = v.FieldByName(a.name)
  289. }
  290. if !rv.IsValid() {
  291. panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name))
  292. }
  293. if _, ok := rv.Interface().(protoreflect.Value); ok {
  294. rv = rv.MethodByName("Interface").Call(nil)[0]
  295. if !rv.IsNil() {
  296. rv = rv.Elem()
  297. }
  298. }
  299. // Ignore zero values.
  300. var isZero bool
  301. switch rv.Kind() {
  302. case reflect.Interface, reflect.Slice:
  303. isZero = rv.IsNil()
  304. case reflect.Bool:
  305. isZero = rv.Bool() == false
  306. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  307. isZero = rv.Int() == 0
  308. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  309. isZero = rv.Uint() == 0
  310. case reflect.String:
  311. isZero = rv.String() == ""
  312. }
  313. if n, ok := rv.Interface().(list); ok {
  314. isZero = n.Len() == 0
  315. }
  316. if isZero {
  317. continue
  318. }
  319. // Format the value.
  320. var s string
  321. v := rv.Interface()
  322. switch v := v.(type) {
  323. case list:
  324. s = formatListOpt(v, false, rs.allowMulti)
  325. case protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor:
  326. s = string(v.(protoreflect.Descriptor).Name())
  327. case protoreflect.Descriptor:
  328. s = string(v.FullName())
  329. case string:
  330. s = strconv.Quote(v)
  331. case []byte:
  332. s = fmt.Sprintf("%q", v)
  333. default:
  334. s = fmt.Sprint(v)
  335. }
  336. rs.recs = append(rs.recs, [2]string{a.name, s})
  337. }
  338. }
  339. func (rs *records) Join() string {
  340. var ss []string
  341. // In single line mode, simply join all records with commas.
  342. if !rs.allowMulti {
  343. for _, r := range rs.recs {
  344. ss = append(ss, r[0]+formatColon(0)+r[1])
  345. }
  346. return joinStrings(ss, false)
  347. }
  348. // In allowMulti line mode, align single line records for more readable output.
  349. var maxLen int
  350. flush := func(i int) {
  351. for _, r := range rs.recs[len(ss):i] {
  352. ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
  353. }
  354. maxLen = 0
  355. }
  356. for i, r := range rs.recs {
  357. if isMulti := strings.Contains(r[1], "\n"); isMulti {
  358. flush(i)
  359. ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
  360. } else if maxLen < len(r[0]) {
  361. maxLen = len(r[0])
  362. }
  363. }
  364. flush(len(rs.recs))
  365. return joinStrings(ss, true)
  366. }
  367. func formatColon(padding int) string {
  368. // Deliberately introduce instability into the debug output to
  369. // discourage users from performing string comparisons.
  370. // This provides us flexibility to change the output in the future.
  371. if detrand.Bool() {
  372. return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0)
  373. } else {
  374. return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020)
  375. }
  376. }
  377. func joinStrings(ss []string, isMulti bool) string {
  378. if len(ss) == 0 {
  379. return ""
  380. }
  381. if isMulti {
  382. return "\n\t" + strings.Join(ss, "\n\t") + "\n"
  383. }
  384. return strings.Join(ss, ", ")
  385. }