packet_stringifier.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package rtcp
  4. import (
  5. "fmt"
  6. "reflect"
  7. )
  8. /*
  9. Converts an RTCP Packet into a human-readable format. The Packets
  10. themselves can control the presentation as follows:
  11. - Fields of a type that have a String() method will be formatted
  12. with that String method (which should not emit '\n' characters)
  13. - Otherwise, fields with a tag containing a "fmt" string will use that
  14. format when serializing the value. For example, to format an SSRC
  15. value as base 16 insted of base 10:
  16. type ExamplePacket struct {
  17. LocalSSRC uint32 `fmt:"0x%X"`
  18. RemotsSSRCs []uint32 `fmt:"%X"`
  19. }
  20. - If no fmt string is present, "%+v" is used by default
  21. The intention of this stringify() function is to simplify creation
  22. of String() methods on new packet types, as it provides a simple
  23. baseline implementation that works well in the majority of cases.
  24. */
  25. func stringify(p Packet) string {
  26. value := reflect.Indirect(reflect.ValueOf(p))
  27. return formatField(value.Type().String(), "", p, "")
  28. }
  29. func formatField(name string, format string, f interface{}, indent string) string { //nolint:gocognit
  30. out := indent
  31. value := reflect.ValueOf(f)
  32. if !value.IsValid() {
  33. return fmt.Sprintf("%s%s: <nil>\n", out, name)
  34. }
  35. isPacket := reflect.TypeOf(f).Implements(reflect.TypeOf((*Packet)(nil)).Elem())
  36. // Resolve pointers to their underlying values
  37. if value.Type().Kind() == reflect.Ptr && !value.IsNil() {
  38. underlying := reflect.Indirect(value)
  39. if underlying.IsValid() {
  40. value = underlying
  41. }
  42. }
  43. // If the field type has a custom String method, use that
  44. // (unless we're a packet, since we want to avoid recursing
  45. // back into this function if the Packet's String() method
  46. // uses it)
  47. if stringMethod := value.MethodByName("String"); !isPacket && stringMethod.IsValid() {
  48. out += fmt.Sprintf("%s: %s\n", name, stringMethod.Call([]reflect.Value{}))
  49. return out
  50. }
  51. switch value.Kind() {
  52. case reflect.Struct:
  53. out += fmt.Sprintf("%s:\n", name)
  54. for i := 0; i < value.NumField(); i++ {
  55. if value.Field(i).CanInterface() {
  56. format = value.Type().Field(i).Tag.Get("fmt")
  57. if format == "" {
  58. format = "%+v"
  59. }
  60. out += formatField(value.Type().Field(i).Name, format, value.Field(i).Interface(), indent+"\t")
  61. }
  62. }
  63. case reflect.Slice:
  64. childKind := value.Type().Elem().Kind()
  65. _, hasStringMethod := value.Type().Elem().MethodByName("String")
  66. if hasStringMethod || childKind == reflect.Struct || childKind == reflect.Ptr || childKind == reflect.Interface || childKind == reflect.Slice {
  67. out += fmt.Sprintf("%s:\n", name)
  68. for i := 0; i < value.Len(); i++ {
  69. childName := fmt.Sprint(i)
  70. // Since interfaces can hold different types of things, we add the
  71. // most specific type name to the name to make it clear what the
  72. // subsequent fields represent.
  73. if value.Index(i).Kind() == reflect.Interface {
  74. childName += fmt.Sprintf(" (%s)", reflect.Indirect(reflect.ValueOf(value.Index(i).Interface())).Type())
  75. }
  76. if value.Index(i).CanInterface() {
  77. out += formatField(childName, format, value.Index(i).Interface(), indent+"\t")
  78. }
  79. }
  80. return out
  81. }
  82. // If we didn't take care of stringing the value already, we fall through to the
  83. // generic case. This will print slices of basic types on a single line.
  84. fallthrough
  85. default:
  86. if value.CanInterface() {
  87. out += fmt.Sprintf("%s: "+format+"\n", name, value.Interface())
  88. }
  89. }
  90. return out
  91. }