print.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package typekit
  2. //package main
  3. import (
  4. "fmt"
  5. "reflect"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "unsafe"
  10. )
  11. const (
  12. colorReset = "\033[0m"
  13. colorStruct = "\033[34m\033[7m"
  14. colorField = "\033[32m"
  15. colorMap = "\033[33m\033[7m"
  16. colorSlice = "\033[35m\033[7m"
  17. colorValue = "\033[31m"
  18. colorKey = "\033[36m"
  19. symbolVert = "│ "
  20. symbolLast = "└─ "
  21. symbolMid = "├─ "
  22. symbolEmpty = " "
  23. )
  24. type PrintOptions struct {
  25. ShowTypes bool
  26. }
  27. func PrettyPrint(v interface{}, opts ...PrintOptions) string {
  28. option := PrintOptions{ShowTypes: true}
  29. if len(opts) > 0 {
  30. option = opts[0]
  31. }
  32. return prettyPrint(reflect.ValueOf(v), 0, make(map[uintptr]bool), option, []bool{}).String()
  33. }
  34. type lines []string
  35. func (l lines) String() string {
  36. return strings.Join(l, "\n")
  37. }
  38. func buildPrefix(indent int, isLasts []bool) string {
  39. var prefix strings.Builder
  40. for i := 0; i < indent; i++ {
  41. if i < len(isLasts) {
  42. if isLasts[i] {
  43. prefix.WriteString(symbolEmpty)
  44. } else {
  45. prefix.WriteString(symbolVert)
  46. }
  47. }
  48. }
  49. return prefix.String()
  50. }
  51. func prettyPrint(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, isLasts []bool) lines {
  52. original := v
  53. for {
  54. switch v.Kind() {
  55. case reflect.Ptr:
  56. if v.IsNil() {
  57. return lines{colorValue + "nil" + colorReset}
  58. }
  59. ptr := v.Pointer()
  60. if visited[ptr] {
  61. return lines{fmt.Sprintf("%s&%p%s", colorValue, v.Interface(), colorReset)}
  62. }
  63. visited[ptr] = true
  64. original = v
  65. v = v.Elem()
  66. case reflect.Interface:
  67. if v.IsNil() {
  68. return lines{colorValue + "nil" + colorReset}
  69. }
  70. v = v.Elem()
  71. default:
  72. goto END_DEREF
  73. }
  74. }
  75. END_DEREF:
  76. switch v.Kind() {
  77. case reflect.Struct:
  78. return printStruct(v, indentLevel, visited, opts, isLasts)
  79. case reflect.Map:
  80. return printMap(v, indentLevel, visited, opts, isLasts)
  81. case reflect.Slice, reflect.Array:
  82. return printSlice(v, indentLevel, visited, opts, isLasts)
  83. case reflect.String:
  84. return lines{fmt.Sprintf("%s%q%s", colorValue, v.String(), colorReset)}
  85. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  86. return lines{fmt.Sprintf("%s%d%s", colorValue, v.Int(), colorReset)}
  87. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  88. return lines{fmt.Sprintf("%s%d%s", colorValue, v.Uint(), colorReset)}
  89. case reflect.Float32, reflect.Float64:
  90. // 使用精确的字符串表示,保留必要精度
  91. str := strconv.FormatFloat(v.Float(), 'f', -1, 64)
  92. // 移除多余的末尾零和小数点
  93. if strings.Contains(str, ".") {
  94. str = strings.TrimRight(str, "0")
  95. str = strings.TrimRight(str, ".")
  96. }
  97. return lines{fmt.Sprintf("%s%s%s", colorValue, str, colorReset)}
  98. case reflect.Bool:
  99. return lines{fmt.Sprintf("%s%t%s", colorValue, v.Bool(), colorReset)}
  100. default:
  101. if original.Kind() == reflect.Ptr {
  102. return lines{fmt.Sprintf("%s%v%s", colorValue, original.Interface(), colorReset)}
  103. }
  104. //return lines{fmt.Sprintf("%s%v%s", colorValue, v.Interface(), colorReset)}
  105. return lines{formatUnexported(v, original)}
  106. }
  107. }
  108. // 新增辅助函数处理未导出字段
  109. func formatUnexported(v, original reflect.Value) string {
  110. var value interface{}
  111. var canShow bool
  112. // 尝试通过不同方式安全获取值
  113. switch {
  114. case v.CanInterface():
  115. value = v.Interface()
  116. canShow = true
  117. case original.Kind() == reflect.Ptr && original.CanInterface():
  118. value = original.Interface()
  119. canShow = true
  120. default:
  121. canShow = false
  122. }
  123. // 特殊处理指针类型
  124. if v.Kind() == reflect.Ptr && !v.IsNil() {
  125. return fmt.Sprintf("%s&%p%s", colorValue, unsafe.Pointer(v.Pointer()), colorReset)
  126. }
  127. // 构造显示字符串
  128. if canShow {
  129. return fmt.Sprintf("%s%v%s", colorValue, value, colorReset)
  130. }
  131. return fmt.Sprintf("%s<unexported>%s", colorValue, colorReset)
  132. }
  133. func printStruct(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
  134. var header string
  135. if opts.ShowTypes {
  136. header = fmt.Sprintf("%s%s%s", colorStruct, v.Type().String(), colorReset)
  137. } else {
  138. header = " ---"
  139. }
  140. result := lines{header}
  141. fieldCount := v.NumField()
  142. for i := 0; i < fieldCount; i++ {
  143. field := v.Type().Field(i)
  144. // 跳过未导出字段
  145. if !field.IsExported() {
  146. continue
  147. }
  148. fieldValue := v.Field(i)
  149. isLast := i == fieldCount-1
  150. prefix := buildPrefix(indentLevel, parentLasts)
  151. currentLasts := append(parentLasts, isLast)
  152. var symbol string
  153. if isLast {
  154. symbol = symbolLast
  155. } else {
  156. symbol = symbolMid
  157. }
  158. fieldLines := prettyPrint(fieldValue, indentLevel+1, visited, opts, currentLasts)
  159. fieldHeader := fmt.Sprintf("%s%s%s%s: ",
  160. prefix,
  161. symbol,
  162. colorField,
  163. field.Name,
  164. )
  165. if len(fieldLines) == 0 {
  166. result = append(result, fieldHeader+colorValue+"<invalid>"+colorReset)
  167. continue
  168. }
  169. result = append(result, fieldHeader+fieldLines[0])
  170. for _, line := range fieldLines[1:] {
  171. result = append(result, prefix+symbolEmpty+line)
  172. }
  173. }
  174. return result
  175. }
  176. func printMap(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
  177. var header string
  178. if opts.ShowTypes {
  179. header = fmt.Sprintf("%s%s%s", colorMap, v.Type().String(), colorReset)
  180. } else {
  181. header = " ---"
  182. }
  183. result := lines{header}
  184. keys := v.MapKeys()
  185. sort.Slice(keys, func(i, j int) bool {
  186. return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
  187. })
  188. keyCount := len(keys)
  189. for i, key := range keys {
  190. prefix := buildPrefix(indentLevel, parentLasts)
  191. isLast := i == keyCount-1
  192. currentLasts := append(parentLasts, isLast)
  193. var symbol string
  194. if isLast {
  195. symbol = symbolLast
  196. } else {
  197. symbol = symbolMid
  198. }
  199. keyLines := prettyPrint(key, 0, visited, opts, currentLasts)
  200. valueLines := prettyPrint(v.MapIndex(key), indentLevel+1, visited, opts, currentLasts)
  201. keyHeader := fmt.Sprintf("%s%s%s%s: ",
  202. prefix,
  203. symbol,
  204. colorKey,
  205. strings.Join(keyLines, ""),
  206. )
  207. if len(valueLines) == 0 {
  208. result = append(result, keyHeader+colorValue+"<invalid>"+colorReset)
  209. continue
  210. }
  211. result = append(result, keyHeader+valueLines[0])
  212. for _, line := range valueLines[1:] {
  213. result = append(result, prefix+symbolEmpty+line)
  214. }
  215. }
  216. return result
  217. }
  218. func printSlice(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
  219. var header string
  220. if opts.ShowTypes {
  221. header = fmt.Sprintf("%s%s%s", colorSlice, v.Type().String(), colorReset)
  222. } else {
  223. header = " ---"
  224. }
  225. result := lines{header}
  226. elemCount := v.Len()
  227. for i := 0; i < elemCount; i++ {
  228. prefix := buildPrefix(indentLevel, parentLasts)
  229. isLast := i == elemCount-1
  230. currentLasts := append(parentLasts, isLast)
  231. var symbol string
  232. if isLast {
  233. symbol = symbolLast
  234. } else {
  235. symbol = symbolMid
  236. }
  237. elemLines := prettyPrint(v.Index(i), indentLevel+1, visited, opts, currentLasts)
  238. bullet := fmt.Sprintf("%s%s", prefix, symbol)
  239. if len(elemLines) == 0 {
  240. result = append(result, bullet+colorValue+"<invalid>"+colorReset)
  241. continue
  242. }
  243. result = append(result, bullet+elemLines[0])
  244. for _, line := range elemLines[1:] {
  245. result = append(result, prefix+symbolEmpty+line)
  246. }
  247. }
  248. return result
  249. }
  250. /*
  251. type Person struct {
  252. Name string
  253. Age int
  254. Hobbies []string
  255. Address struct {
  256. City string
  257. State string
  258. }
  259. Metadata map[string]interface{}
  260. }
  261. func main() {
  262. p := Person{
  263. Name: "Alice",
  264. Age: 30,
  265. Hobbies: []string{"Reading", "Hiking"},
  266. Metadata: map[string]interface{}{
  267. "id": 123,
  268. "scores": []float64{98.5, 87.2},
  269. "nested": map[string]interface{}{"a": 1, "b": 2},
  270. },
  271. }
  272. p.Address.City = "New York"
  273. p.Address.State = "NY"
  274. p.Metadata["self_ref"] = &p
  275. fmt.Println("With types:")
  276. fmt.Println(PrettyPrint(p, PrintOptions{ShowTypes: false}))
  277. fmt.Println("\nWithout types:\n")
  278. fmt.Println(PrettyPrint(p))
  279. }
  280. */