// Copyright (c) 2015 Arista Networks, Inc. // Use of this source code is governed by the Apache License 2.0 // that can be found in the COPYING file. package test import ( "fmt" "os" "reflect" "sort" "strconv" "github.com/aristanetworks/goarista/areflect" ) // PrettyPrint tries to display a human readable version of an interface func PrettyPrint(v interface{}) string { return PrettyPrintWithDepth(v, prettyPrintDepth) } // PrettyPrintWithDepth tries to display a human readable version of an interface // and allows to define the depth of the print func PrettyPrintWithDepth(v interface{}, depth int) string { return prettyPrint(reflect.ValueOf(v), ptrSet{}, depth) } var prettyPrintDepth = 8 func init() { d := os.Getenv("PPDEPTH") if d, ok := strconv.Atoi(d); ok == nil && d >= 0 { prettyPrintDepth = d } } type ptrSet map[uintptr]struct{} func prettyPrint(v reflect.Value, done ptrSet, depth int) string { return prettyPrintWithType(v, done, depth, true) } func prettyPrintWithType(v reflect.Value, done ptrSet, depth int, showType bool) string { if depth < 0 { return "" } switch v.Kind() { case reflect.Invalid: return "nil" case reflect.Bool: return fmt.Sprintf("%t", v.Bool()) case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Int, reflect.Uint, reflect.Uintptr, reflect.Complex64, reflect.Complex128: i := areflect.ForceExport(v).Interface() if showType { return fmt.Sprintf("%s(%v)", v.Type().Name(), i) } return fmt.Sprintf("%v", i) case reflect.String: return fmt.Sprintf("%q", v.String()) case reflect.Ptr: return "*" + prettyPrintWithType(v.Elem(), done, depth-1, showType) case reflect.Interface: return prettyPrintWithType(v.Elem(), done, depth-1, showType) case reflect.Map: var r []byte r = append(r, []byte(v.Type().String())...) r = append(r, '{') var elems mapEntries for _, k := range v.MapKeys() { elem := &mapEntry{ k: prettyPrint(k, done, depth-1), v: prettyPrint(v.MapIndex(k), done, depth-1), } elems.entries = append(elems.entries, elem) } sort.Sort(&elems) for i, e := range elems.entries { if i > 0 { r = append(r, []byte(", ")...) } r = append(r, []byte(e.k)...) r = append(r, ':') r = append(r, []byte(e.v)...) } r = append(r, '}') return string(r) case reflect.Struct: // Circular dependency? if v.CanAddr() { ptr := v.UnsafeAddr() if _, ok := done[ptr]; ok { return fmt.Sprintf("%s{}", v.Type().String()) } done[ptr] = struct{}{} } var r []byte r = append(r, []byte(v.Type().String())...) r = append(r, '{') for i := 0; i < v.NumField(); i++ { if i > 0 { r = append(r, []byte(", ")...) } sf := v.Type().Field(i) r = append(r, sf.Name...) r = append(r, ':') r = append(r, prettyPrint(v.Field(i), done, depth-1)...) } r = append(r, '}') return string(r) case reflect.Chan: var ptr, bufsize string if v.Pointer() == 0 { ptr = "nil" } else { ptr = fmt.Sprintf("0x%x", v.Pointer()) } if v.Cap() > 0 { bufsize = fmt.Sprintf("[%d]", v.Cap()) } return fmt.Sprintf("(%s)(%s)%s", v.Type().String(), ptr, bufsize) case reflect.Func: return "func(...)" case reflect.Array, reflect.Slice: l := v.Len() var r []byte if v.Type().Elem().Kind() == reflect.Uint8 && v.Kind() != reflect.Array { b := areflect.ForceExport(v).Interface().([]byte) r = append(r, []byte(`[]byte(`)...) if b == nil { r = append(r, []byte("nil")...) } else { r = append(r, []byte(fmt.Sprintf("%q", b))...) } r = append(r, ')') return string(r) } r = append(r, []byte(v.Type().String())...) r = append(r, '{') for i := 0; i < l; i++ { if i > 0 { r = append(r, []byte(", ")...) } r = append(r, prettyPrintWithType(v.Index(i), done, depth-1, false)...) } r = append(r, '}') return string(r) case reflect.UnsafePointer: var ptr string if v.Pointer() == 0 { ptr = "nil" } else { ptr = fmt.Sprintf("0x%x", v.Pointer()) } if showType { ptr = fmt.Sprintf("(unsafe.Pointer)(%s)", ptr) } return ptr default: panic(fmt.Errorf("Unhandled kind of reflect.Value: %v", v.Kind())) } }