github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/test/pretty.go (about)

     1  // Copyright (c) 2015 Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package test
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  	"sort"
    12  	"strconv"
    13  
    14  	"github.com/aristanetworks/goarista/areflect"
    15  )
    16  
    17  // PrettyPrint tries to display a human readable version of an interface
    18  func PrettyPrint(v interface{}) string {
    19  	return PrettyPrintWithDepth(v, prettyPrintDepth)
    20  }
    21  
    22  // PrettyPrintWithDepth tries to display a human readable version of an interface
    23  // and allows to define the depth of the print
    24  func PrettyPrintWithDepth(v interface{}, depth int) string {
    25  	return prettyPrint(reflect.ValueOf(v), ptrSet{}, depth)
    26  }
    27  
    28  var prettyPrintDepth = 8
    29  
    30  func init() {
    31  	d := os.Getenv("PPDEPTH")
    32  	if d, ok := strconv.Atoi(d); ok == nil && d >= 0 {
    33  		prettyPrintDepth = d
    34  	}
    35  }
    36  
    37  type ptrSet map[uintptr]struct{}
    38  
    39  func prettyPrint(v reflect.Value, done ptrSet, depth int) string {
    40  	return prettyPrintWithType(v, done, depth, true)
    41  }
    42  
    43  func prettyPrintWithType(v reflect.Value, done ptrSet, depth int, showType bool) string {
    44  	if depth < 0 {
    45  		return "<max_depth>"
    46  	}
    47  	switch v.Kind() {
    48  	case reflect.Invalid:
    49  		return "nil"
    50  	case reflect.Bool:
    51  		return fmt.Sprintf("%t", v.Bool())
    52  	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
    53  		reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    54  		reflect.Float32, reflect.Float64,
    55  		reflect.Int, reflect.Uint, reflect.Uintptr,
    56  		reflect.Complex64, reflect.Complex128:
    57  		i := areflect.ForceExport(v).Interface()
    58  		if showType {
    59  			return fmt.Sprintf("%s(%v)", v.Type().Name(), i)
    60  		}
    61  		return fmt.Sprintf("%v", i)
    62  	case reflect.String:
    63  		return fmt.Sprintf("%q", v.String())
    64  	case reflect.Ptr:
    65  		return "*" + prettyPrintWithType(v.Elem(), done, depth-1, showType)
    66  	case reflect.Interface:
    67  		return prettyPrintWithType(v.Elem(), done, depth-1, showType)
    68  	case reflect.Map:
    69  		var r []byte
    70  		r = append(r, []byte(v.Type().String())...)
    71  		r = append(r, '{')
    72  		var elems mapEntries
    73  		for _, k := range v.MapKeys() {
    74  			elem := &mapEntry{
    75  				k: prettyPrint(k, done, depth-1),
    76  				v: prettyPrint(v.MapIndex(k), done, depth-1),
    77  			}
    78  			elems.entries = append(elems.entries, elem)
    79  		}
    80  		sort.Sort(&elems)
    81  		for i, e := range elems.entries {
    82  			if i > 0 {
    83  				r = append(r, []byte(", ")...)
    84  			}
    85  			r = append(r, []byte(e.k)...)
    86  			r = append(r, ':')
    87  			r = append(r, []byte(e.v)...)
    88  		}
    89  		r = append(r, '}')
    90  		return string(r)
    91  	case reflect.Struct:
    92  		// Circular dependency?
    93  		if v.CanAddr() {
    94  			ptr := v.UnsafeAddr()
    95  			if _, ok := done[ptr]; ok {
    96  				return fmt.Sprintf("%s{<circular dependency>}", v.Type().String())
    97  			}
    98  			done[ptr] = struct{}{}
    99  		}
   100  		var r []byte
   101  		r = append(r, []byte(v.Type().String())...)
   102  		r = append(r, '{')
   103  		for i := 0; i < v.NumField(); i++ {
   104  			if i > 0 {
   105  				r = append(r, []byte(", ")...)
   106  			}
   107  			sf := v.Type().Field(i)
   108  			r = append(r, sf.Name...)
   109  			r = append(r, ':')
   110  			r = append(r, prettyPrint(v.Field(i), done, depth-1)...)
   111  		}
   112  		r = append(r, '}')
   113  		return string(r)
   114  	case reflect.Chan:
   115  		var ptr, bufsize string
   116  		if v.Pointer() == 0 {
   117  			ptr = "nil"
   118  		} else {
   119  			ptr = fmt.Sprintf("0x%x", v.Pointer())
   120  		}
   121  		if v.Cap() > 0 {
   122  			bufsize = fmt.Sprintf("[%d]", v.Cap())
   123  		}
   124  		return fmt.Sprintf("(%s)(%s)%s", v.Type().String(), ptr, bufsize)
   125  	case reflect.Func:
   126  		return "func(...)"
   127  	case reflect.Array, reflect.Slice:
   128  		l := v.Len()
   129  		var r []byte
   130  		if v.Type().Elem().Kind() == reflect.Uint8 && v.Kind() != reflect.Array {
   131  			b := areflect.ForceExport(v).Interface().([]byte)
   132  			r = append(r, []byte(`[]byte(`)...)
   133  			if b == nil {
   134  				r = append(r, []byte("nil")...)
   135  			} else {
   136  				r = append(r, []byte(fmt.Sprintf("%q", b))...)
   137  			}
   138  			r = append(r, ')')
   139  			return string(r)
   140  		}
   141  		r = append(r, []byte(v.Type().String())...)
   142  		r = append(r, '{')
   143  		for i := 0; i < l; i++ {
   144  			if i > 0 {
   145  				r = append(r, []byte(", ")...)
   146  			}
   147  			r = append(r, prettyPrintWithType(v.Index(i), done, depth-1, false)...)
   148  		}
   149  		r = append(r, '}')
   150  		return string(r)
   151  	case reflect.UnsafePointer:
   152  		var ptr string
   153  		if v.Pointer() == 0 {
   154  			ptr = "nil"
   155  		} else {
   156  			ptr = fmt.Sprintf("0x%x", v.Pointer())
   157  		}
   158  		if showType {
   159  			ptr = fmt.Sprintf("(unsafe.Pointer)(%s)", ptr)
   160  		}
   161  		return ptr
   162  	default:
   163  		panic(fmt.Errorf("Unhandled kind of reflect.Value: %v", v.Kind()))
   164  	}
   165  }