github.com/Psiphon-Inc/goarista@v0.0.0-20160825065156-d002785f4c67/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 }