stringer.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. }...)
  241. case protoreflect.EnumValueDescriptor:
  242. rs.Append(rv, []methodAndName{
  243. {rv.MethodByName("Number"), "Number"},
  244. }...)
  245. case protoreflect.ServiceDescriptor:
  246. rs.Append(rv, []methodAndName{
  247. {rv.MethodByName("Methods"), "Methods"},
  248. }...)
  249. case protoreflect.MethodDescriptor:
  250. rs.Append(rv, []methodAndName{
  251. {rv.MethodByName("Input"), "Input"},
  252. {rv.MethodByName("Output"), "Output"},
  253. {rv.MethodByName("IsStreamingClient"), "IsStreamingClient"},
  254. {rv.MethodByName("IsStreamingServer"), "IsStreamingServer"},
  255. }...)
  256. }
  257. if m := rv.MethodByName("GoType"); m.IsValid() {
  258. rs.Append(rv, methodAndName{m, "GoType"})
  259. }
  260. }
  261. return start + rs.Join() + end
  262. }
  263. type records struct {
  264. recs [][2]string
  265. allowMulti bool
  266. // record is a function that will be called for every Append() or
  267. // AppendRecs() call, to be used for testing with the
  268. // InternalFormatDescOptForTesting function.
  269. record func(string)
  270. }
  271. func (rs *records) AppendRecs(fieldName string, newRecs [2]string) {
  272. if rs.record != nil {
  273. rs.record(fieldName)
  274. }
  275. rs.recs = append(rs.recs, newRecs)
  276. }
  277. func (rs *records) Append(v reflect.Value, accessors ...methodAndName) {
  278. for _, a := range accessors {
  279. if rs.record != nil {
  280. rs.record(a.name)
  281. }
  282. var rv reflect.Value
  283. if a.method.IsValid() {
  284. rv = a.method.Call(nil)[0]
  285. }
  286. if v.Kind() == reflect.Struct && !rv.IsValid() {
  287. rv = v.FieldByName(a.name)
  288. }
  289. if !rv.IsValid() {
  290. panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name))
  291. }
  292. if _, ok := rv.Interface().(protoreflect.Value); ok {
  293. rv = rv.MethodByName("Interface").Call(nil)[0]
  294. if !rv.IsNil() {
  295. rv = rv.Elem()
  296. }
  297. }
  298. // Ignore zero values.
  299. var isZero bool
  300. switch rv.Kind() {
  301. case reflect.Interface, reflect.Slice:
  302. isZero = rv.IsNil()
  303. case reflect.Bool:
  304. isZero = rv.Bool() == false
  305. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  306. isZero = rv.Int() == 0
  307. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  308. isZero = rv.Uint() == 0
  309. case reflect.String:
  310. isZero = rv.String() == ""
  311. }
  312. if n, ok := rv.Interface().(list); ok {
  313. isZero = n.Len() == 0
  314. }
  315. if isZero {
  316. continue
  317. }
  318. // Format the value.
  319. var s string
  320. v := rv.Interface()
  321. switch v := v.(type) {
  322. case list:
  323. s = formatListOpt(v, false, rs.allowMulti)
  324. case protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor:
  325. s = string(v.(protoreflect.Descriptor).Name())
  326. case protoreflect.Descriptor:
  327. s = string(v.FullName())
  328. case string:
  329. s = strconv.Quote(v)
  330. case []byte:
  331. s = fmt.Sprintf("%q", v)
  332. default:
  333. s = fmt.Sprint(v)
  334. }
  335. rs.recs = append(rs.recs, [2]string{a.name, s})
  336. }
  337. }
  338. func (rs *records) Join() string {
  339. var ss []string
  340. // In single line mode, simply join all records with commas.
  341. if !rs.allowMulti {
  342. for _, r := range rs.recs {
  343. ss = append(ss, r[0]+formatColon(0)+r[1])
  344. }
  345. return joinStrings(ss, false)
  346. }
  347. // In allowMulti line mode, align single line records for more readable output.
  348. var maxLen int
  349. flush := func(i int) {
  350. for _, r := range rs.recs[len(ss):i] {
  351. ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
  352. }
  353. maxLen = 0
  354. }
  355. for i, r := range rs.recs {
  356. if isMulti := strings.Contains(r[1], "\n"); isMulti {
  357. flush(i)
  358. ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
  359. } else if maxLen < len(r[0]) {
  360. maxLen = len(r[0])
  361. }
  362. }
  363. flush(len(rs.recs))
  364. return joinStrings(ss, true)
  365. }
  366. func formatColon(padding int) string {
  367. // Deliberately introduce instability into the debug output to
  368. // discourage users from performing string comparisons.
  369. // This provides us flexibility to change the output in the future.
  370. if detrand.Bool() {
  371. return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0)
  372. } else {
  373. return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020)
  374. }
  375. }
  376. func joinStrings(ss []string, isMulti bool) string {
  377. if len(ss) == 0 {
  378. return ""
  379. }
  380. if isMulti {
  381. return "\n\t" + strings.Join(ss, "\n\t") + "\n"
  382. }
  383. return strings.Join(ss, ", ")
  384. }