123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- package typekit
- //package main
- import (
- "fmt"
- "reflect"
- "sort"
- "strconv"
- "strings"
- "unsafe"
- )
- const (
- colorReset = "\033[0m"
- colorStruct = "\033[34m\033[7m"
- colorField = "\033[32m"
- colorMap = "\033[33m\033[7m"
- colorSlice = "\033[35m\033[7m"
- colorValue = "\033[31m"
- colorKey = "\033[36m"
- symbolVert = "│ "
- symbolLast = "└─ "
- symbolMid = "├─ "
- symbolEmpty = " "
- )
- type PrintOptions struct {
- ShowTypes bool
- }
- func PrettyPrint(v interface{}, opts ...PrintOptions) string {
- option := PrintOptions{ShowTypes: true}
- if len(opts) > 0 {
- option = opts[0]
- }
- return prettyPrint(reflect.ValueOf(v), 0, make(map[uintptr]bool), option, []bool{}).String()
- }
- type lines []string
- func (l lines) String() string {
- return strings.Join(l, "\n")
- }
- func buildPrefix(indent int, isLasts []bool) string {
- var prefix strings.Builder
- for i := 0; i < indent; i++ {
- if i < len(isLasts) {
- if isLasts[i] {
- prefix.WriteString(symbolEmpty)
- } else {
- prefix.WriteString(symbolVert)
- }
- }
- }
- return prefix.String()
- }
- func prettyPrint(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, isLasts []bool) lines {
- original := v
- for {
- switch v.Kind() {
- case reflect.Ptr:
- if v.IsNil() {
- return lines{colorValue + "nil" + colorReset}
- }
- ptr := v.Pointer()
- if visited[ptr] {
- return lines{fmt.Sprintf("%s&%p%s", colorValue, v.Interface(), colorReset)}
- }
- visited[ptr] = true
- original = v
- v = v.Elem()
- case reflect.Interface:
- if v.IsNil() {
- return lines{colorValue + "nil" + colorReset}
- }
- v = v.Elem()
- default:
- goto END_DEREF
- }
- }
- END_DEREF:
- switch v.Kind() {
- case reflect.Struct:
- return printStruct(v, indentLevel, visited, opts, isLasts)
- case reflect.Map:
- return printMap(v, indentLevel, visited, opts, isLasts)
- case reflect.Slice, reflect.Array:
- return printSlice(v, indentLevel, visited, opts, isLasts)
- case reflect.String:
- return lines{fmt.Sprintf("%s%q%s", colorValue, v.String(), colorReset)}
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return lines{fmt.Sprintf("%s%d%s", colorValue, v.Int(), colorReset)}
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return lines{fmt.Sprintf("%s%d%s", colorValue, v.Uint(), colorReset)}
- case reflect.Float32, reflect.Float64:
- // 使用精确的字符串表示,保留必要精度
- str := strconv.FormatFloat(v.Float(), 'f', -1, 64)
- // 移除多余的末尾零和小数点
- if strings.Contains(str, ".") {
- str = strings.TrimRight(str, "0")
- str = strings.TrimRight(str, ".")
- }
- return lines{fmt.Sprintf("%s%s%s", colorValue, str, colorReset)}
- case reflect.Bool:
- return lines{fmt.Sprintf("%s%t%s", colorValue, v.Bool(), colorReset)}
- default:
- if original.Kind() == reflect.Ptr {
- return lines{fmt.Sprintf("%s%v%s", colorValue, original.Interface(), colorReset)}
- }
- //return lines{fmt.Sprintf("%s%v%s", colorValue, v.Interface(), colorReset)}
- return lines{formatUnexported(v, original)}
- }
- }
- // 新增辅助函数处理未导出字段
- func formatUnexported(v, original reflect.Value) string {
- var value interface{}
- var canShow bool
- // 尝试通过不同方式安全获取值
- switch {
- case v.CanInterface():
- value = v.Interface()
- canShow = true
- case original.Kind() == reflect.Ptr && original.CanInterface():
- value = original.Interface()
- canShow = true
- default:
- canShow = false
- }
- // 特殊处理指针类型
- if v.Kind() == reflect.Ptr && !v.IsNil() {
- return fmt.Sprintf("%s&%p%s", colorValue, unsafe.Pointer(v.Pointer()), colorReset)
- }
- // 构造显示字符串
- if canShow {
- return fmt.Sprintf("%s%v%s", colorValue, value, colorReset)
- }
- return fmt.Sprintf("%s<unexported>%s", colorValue, colorReset)
- }
- func printStruct(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
- var header string
- if opts.ShowTypes {
- header = fmt.Sprintf("%s%s%s", colorStruct, v.Type().String(), colorReset)
- } else {
- header = " ---"
- }
- result := lines{header}
- fieldCount := v.NumField()
- for i := 0; i < fieldCount; i++ {
- field := v.Type().Field(i)
- // 跳过未导出字段
- if !field.IsExported() {
- continue
- }
- fieldValue := v.Field(i)
- isLast := i == fieldCount-1
- prefix := buildPrefix(indentLevel, parentLasts)
- currentLasts := append(parentLasts, isLast)
- var symbol string
- if isLast {
- symbol = symbolLast
- } else {
- symbol = symbolMid
- }
- fieldLines := prettyPrint(fieldValue, indentLevel+1, visited, opts, currentLasts)
- fieldHeader := fmt.Sprintf("%s%s%s%s: ",
- prefix,
- symbol,
- colorField,
- field.Name,
- )
- if len(fieldLines) == 0 {
- result = append(result, fieldHeader+colorValue+"<invalid>"+colorReset)
- continue
- }
- result = append(result, fieldHeader+fieldLines[0])
- for _, line := range fieldLines[1:] {
- result = append(result, prefix+symbolEmpty+line)
- }
- }
- return result
- }
- func printMap(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
- var header string
- if opts.ShowTypes {
- header = fmt.Sprintf("%s%s%s", colorMap, v.Type().String(), colorReset)
- } else {
- header = " ---"
- }
- result := lines{header}
- keys := v.MapKeys()
- sort.Slice(keys, func(i, j int) bool {
- return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
- })
- keyCount := len(keys)
- for i, key := range keys {
- prefix := buildPrefix(indentLevel, parentLasts)
- isLast := i == keyCount-1
- currentLasts := append(parentLasts, isLast)
- var symbol string
- if isLast {
- symbol = symbolLast
- } else {
- symbol = symbolMid
- }
- keyLines := prettyPrint(key, 0, visited, opts, currentLasts)
- valueLines := prettyPrint(v.MapIndex(key), indentLevel+1, visited, opts, currentLasts)
- keyHeader := fmt.Sprintf("%s%s%s%s: ",
- prefix,
- symbol,
- colorKey,
- strings.Join(keyLines, ""),
- )
- if len(valueLines) == 0 {
- result = append(result, keyHeader+colorValue+"<invalid>"+colorReset)
- continue
- }
- result = append(result, keyHeader+valueLines[0])
- for _, line := range valueLines[1:] {
- result = append(result, prefix+symbolEmpty+line)
- }
- }
- return result
- }
- func printSlice(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
- var header string
- if opts.ShowTypes {
- header = fmt.Sprintf("%s%s%s", colorSlice, v.Type().String(), colorReset)
- } else {
- header = " ---"
- }
- result := lines{header}
- elemCount := v.Len()
- for i := 0; i < elemCount; i++ {
- prefix := buildPrefix(indentLevel, parentLasts)
- isLast := i == elemCount-1
- currentLasts := append(parentLasts, isLast)
- var symbol string
- if isLast {
- symbol = symbolLast
- } else {
- symbol = symbolMid
- }
- elemLines := prettyPrint(v.Index(i), indentLevel+1, visited, opts, currentLasts)
- bullet := fmt.Sprintf("%s%s", prefix, symbol)
- if len(elemLines) == 0 {
- result = append(result, bullet+colorValue+"<invalid>"+colorReset)
- continue
- }
- result = append(result, bullet+elemLines[0])
- for _, line := range elemLines[1:] {
- result = append(result, prefix+symbolEmpty+line)
- }
- }
- return result
- }
- /*
- type Person struct {
- Name string
- Age int
- Hobbies []string
- Address struct {
- City string
- State string
- }
- Metadata map[string]interface{}
- }
- func main() {
- p := Person{
- Name: "Alice",
- Age: 30,
- Hobbies: []string{"Reading", "Hiking"},
- Metadata: map[string]interface{}{
- "id": 123,
- "scores": []float64{98.5, 87.2},
- "nested": map[string]interface{}{"a": 1, "b": 2},
- },
- }
- p.Address.City = "New York"
- p.Address.State = "NY"
- p.Metadata["self_ref"] = &p
- fmt.Println("With types:")
- fmt.Println(PrettyPrint(p, PrintOptions{ShowTypes: false}))
- fmt.Println("\nWithout types:\n")
- fmt.Println(PrettyPrint(p))
- }
- */
|