123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- package main
- import (
- "fmt"
- "os"
- "reflect"
- "sort"
- "strconv"
- "strings"
- "github.com/muesli/termenv"
- )
- // 颜色配置枚举
- type ColorProfile int
- const (
- Auto ColorProfile = iota
- Dark
- Light
- NoColor
- )
- // 打印选项结构
- type PrintOptions struct {
- ShowTypes bool // 是否显示类型信息
- ColorProfile ColorProfile // 颜色配置方案
- FloatPrecision int // 浮点数精度(-1表示自动)
- }
- // 颜色缓存变量
- var (
- colorType string
- colorField string
- colorKey string
- colorValue string
- resetColor string
- )
- // 初始化颜色配置
- func initColors(profile ColorProfile) {
- output := termenv.NewOutput(os.Stdout)
- style := output.String()
- resetColor = style.Reset().String()
- switch profile {
- case Auto:
- if output.HasDarkBackground() {
- setDarkThemeColors(output)
- } else {
- setLightThemeColors(output)
- }
- case Dark:
- setDarkThemeColors(output)
- case Light:
- setLightThemeColors(output)
- case NoColor:
- colorType = ""
- colorField = ""
- colorKey = ""
- colorValue = ""
- resetColor = ""
- }
- }
- // 暗色主题配色
- func setDarkThemeColors(output *termenv.Output) {
- colorType = output.String().Foreground(output.Color("#5FAFFF")).String()
- colorField = output.String().Foreground(output.Color("#AFF080")).String()
- colorKey = output.String().Foreground(output.Color("#FFD700")).String()
- colorValue = output.String().Foreground(output.Color("#E0E0E0")).String()
- }
- // 亮色主题配色
- func setLightThemeColors(output *termenv.Output) {
- colorType = output.String().Foreground(output.Color("#005FAF")).String()
- colorField = output.String().Foreground(output.Color("#008000")).String()
- colorKey = output.String().Foreground(output.Color("#AF5F00")).String()
- colorValue = output.String().Foreground(output.Color("#404040")).String()
- }
- // 美观打印入口函数
- func PrettyPrint(v interface{}, opts ...PrintOptions) string {
- // 设置默认选项
- option := PrintOptions{
- ShowTypes: true,
- ColorProfile: Auto,
- FloatPrecision: -1,
- }
- if len(opts) > 0 {
- option = opts[0]
- }
- // 初始化颜色
- initColors(option.ColorProfile)
- // 开始递归打印
- 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 prettyPrint(v reflect.Value, depth int, visited map[uintptr]bool, opts PrintOptions, lastMarkers []bool) lines {
- original := v
- // 处理指针和接口
- for {
- switch v.Kind() {
- case reflect.Ptr:
- if v.IsNil() {
- return lines{colorValue + "nil" + resetColor}
- }
- ptr := v.Pointer()
- if visited[ptr] {
- return lines{fmt.Sprintf("%s&%p%s", colorValue, v.Interface(), resetColor)}
- }
- visited[ptr] = true
- original = v
- v = v.Elem()
- case reflect.Interface:
- if v.IsNil() {
- return lines{colorValue + "nil" + resetColor}
- }
- v = v.Elem()
- default:
- goto END_DEREF
- }
- }
- END_DEREF:
- var result lines
- prefix := buildPrefix(depth, lastMarkers)
- // 添加类型行
- if opts.ShowTypes && depth == 0 {
- typeLine := fmt.Sprintf("%s%s%s", colorType, getTypeName(original, v), resetColor)
- result = append(result, typeLine)
- }
- switch v.Kind() {
- case reflect.Struct:
- result = append(result, printStruct(v, depth, visited, opts, lastMarkers)...)
- case reflect.Map:
- result = append(result, printMap(v, depth, visited, opts, lastMarkers)...)
- case reflect.Slice, reflect.Array:
- result = append(result, printSlice(v, depth, visited, opts, lastMarkers)...)
- default:
- result = append(result, formatValue(v, prefix, opts))
- }
- return result
- }
- // 构建前缀字符串
- func buildPrefix(depth int, lastMarkers []bool) string {
- if depth == 0 {
- return ""
- }
- var prefix strings.Builder
- for i := 0; i < depth-1; i++ {
- if i < len(lastMarkers) && lastMarkers[i] {
- prefix.WriteString(" ")
- } else {
- prefix.WriteString("│ ")
- }
- }
- if depth > 0 {
- if len(lastMarkers) > 0 && lastMarkers[len(lastMarkers)-1] {
- prefix.WriteString("└── ")
- } else {
- prefix.WriteString("├── ")
- }
- }
- return prefix.String()
- }
- // 获取类型名称
- func getTypeName(original, v reflect.Value) string {
- if original.Kind() == reflect.Ptr {
- return original.Type().String()
- }
- return v.Type().String()
- }
- // 格式化值
- func formatValue(v reflect.Value, prefix string, opts PrintOptions) string {
- switch v.Kind() {
- case reflect.String:
- return fmt.Sprintf("%s%s%q%s", prefix, colorValue, v.String(), resetColor)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return fmt.Sprintf("%s%s%d%s", prefix, colorValue, v.Int(), resetColor)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return fmt.Sprintf("%s%s%d%s", prefix, colorValue, v.Uint(), resetColor)
- case reflect.Float32, reflect.Float64:
- return formatFloat(v.Float(), opts.FloatPrecision, prefix)
- case reflect.Bool:
- return fmt.Sprintf("%s%s%t%s", prefix, colorValue, v.Bool(), resetColor)
- default:
- return fmt.Sprintf("%s%s%v%s", prefix, colorValue, v.Interface(), resetColor)
- }
- }
- // 格式化浮点数(改进精度处理)
- func formatFloat(f float64, precision int, prefix string) string {
- var str string
- if precision >= 0 {
- str = strconv.FormatFloat(f, 'f', precision, 64)
- } else {
- // 自动处理精度
- str = strconv.FormatFloat(f, 'f', -1, 64)
- if strings.Contains(str, ".") {
- str = strings.TrimRight(str, "0")
- str = strings.TrimRight(str, ".")
- }
- }
- return fmt.Sprintf("%s%s%s%s", prefix, colorValue, str, resetColor)
- }
- // 打印结构体
- func printStruct(v reflect.Value, depth int, visited map[uintptr]bool, opts PrintOptions, lastMarkers []bool) lines {
- var result lines
- fieldCount := v.NumField()
- for i := 0; i < fieldCount; i++ {
- field := v.Type().Field(i)
- fieldValue := v.Field(i)
- isLast := i == fieldCount-1
- newMarkers := append(lastMarkers, isLast)
- subLines := prettyPrint(fieldValue, depth+1, visited, opts, newMarkers)
- // 字段头
- fieldHeader := fmt.Sprintf("%s%s%s:",
- buildPrefix(depth+1, newMarkers[:len(newMarkers)-1]),
- colorField,
- field.Name,
- )
- if len(subLines) == 0 {
- result = append(result, fieldHeader+colorValue+"<invalid>"+resetColor)
- continue
- }
- // 合并字段头和数据
- result = append(result, fieldHeader+strings.TrimPrefix(subLines[0], buildPrefix(depth+1, newMarkers)))
- result = append(result, subLines[1:]...)
- }
- return result
- }
- // 打印Map
- func printMap(v reflect.Value, depth int, visited map[uintptr]bool, opts PrintOptions, lastMarkers []bool) lines {
- var result lines
- keys := v.MapKeys()
- sort.Slice(keys, func(i, j int) bool {
- return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
- })
- for i, key := range keys {
- isLast := i == len(keys)-1
- newMarkers := append(lastMarkers, isLast)
- keyLines := prettyPrint(key, depth+1, visited, opts, newMarkers)
- valueLines := prettyPrint(v.MapIndex(key), depth+1, visited, opts, newMarkers)
- // Key头
- keyHeader := fmt.Sprintf("%s%s%s:",
- buildPrefix(depth+1, newMarkers[:len(newMarkers)-1]),
- colorKey,
- strings.Join(keyLines, ""),
- )
- // 合并Key和Value
- if len(valueLines) > 0 {
- result = append(result, keyHeader+strings.TrimPrefix(valueLines[0], buildPrefix(depth+1, newMarkers)))
- result = append(result, valueLines[1:]...)
- } else {
- result = append(result, keyHeader+"<invalid>")
- }
- }
- return result
- }
- // 打印Slice/Array
- func printSlice(v reflect.Value, depth int, visited map[uintptr]bool, opts PrintOptions, lastMarkers []bool) lines {
- var result lines
- for i := 0; i < v.Len(); i++ {
- isLast := i == v.Len()-1
- newMarkers := append(lastMarkers, isLast)
- elemLines := prettyPrint(v.Index(i), depth+1, visited, opts, newMarkers)
- result = append(result, elemLines...)
- }
- 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.2000, 100.0},
- "nested": map[string]interface{}{"a": 1, "b": 2},
- },
- }
- p.Address.City = "New York"
- p.Address.State = "NY"
- p.Metadata["self_ref"] = &p
- fmt.Println("=== 默认输出(自动颜色适配)===")
- fmt.Println(PrettyPrint(p))
- fmt.Println("\n=== 显示类型+亮色主题+浮点精度2位 ===")
- fmt.Println(PrettyPrint(p, PrintOptions{
- ShowTypes: true,
- ColorProfile: Light,
- FloatPrecision: 2,
- }))
- fmt.Println("\n=== 不显示类型+暗色主题 ===")
- fmt.Println(PrettyPrint(p, PrintOptions{
- ShowTypes: false,
- ColorProfile: Dark,
- }))
- }
|