Skip to content

Commit

Permalink
Add support for rendering unexported struct fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalloc committed Jan 19, 2019
1 parent 740151a commit afa9bdf
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 34 deletions.
21 changes: 13 additions & 8 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@ func (c *context) visit(
defer iago.Recover(&err)

switch rv.Kind() {
case reflect.String, reflect.Bool:
// note that type names are never included for these types, as they can never
// be ambiguous
c.writef(w, "%#v", rv.Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
c.write(w, formatNumber(rv, knownType))
// type name is not rendered for these types, as the literals are unambiguous.
case reflect.String:
c.writef(w, "%#v", rv.String())
case reflect.Bool:
c.writef(w, "%#v", rv.Bool())

// the rest of the types can be amgiuous unless type information is included.
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
c.write(w, formatInt(rv, knownType))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
c.write(w, formatUint(rv, knownType))
case reflect.Float32, reflect.Float64:
c.write(w, formatFloat(rv, knownType))
case reflect.Complex64, reflect.Complex128:
c.write(w, formatComplex(rv, knownType))
case reflect.Uintptr:
Expand Down
49 changes: 38 additions & 11 deletions shallow.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"reflect"
)

// formatNumber formats integers and floating point numbers.
func formatNumber(rv reflect.Value, knownType bool) string {
s := fmt.Sprintf("%v", rv.Interface())
// formatInt formats signed integers.
func formatInt(rv reflect.Value, knownType bool) string {
s := fmt.Sprintf("%v", rv.Int())

if knownType {
return s
Expand All @@ -20,23 +20,50 @@ func formatNumber(rv reflect.Value, knownType bool) string {
)
}

// formatComplex formats complex numbers.
func formatComplex(rv reflect.Value, knownType bool) string {
// formatUint formats unsigned integers.
func formatUint(rv reflect.Value, knownType bool) string {
s := fmt.Sprintf("%v", rv.Uint())

if knownType {
s := fmt.Sprintf("%v", rv.Interface())
return s[1 : len(s)-1] // trim the opening and closing parenthesis
return s
}

return fmt.Sprintf(
"%s%v",
"%s(%s)",
formatTypeName(rv.Type()),
rv.Interface(),
s,
)
}

// formatFloat formats floating point numbers.
func formatFloat(rv reflect.Value, knownType bool) string {
s := fmt.Sprintf("%v", rv.Float())

if knownType {
return s
}

return fmt.Sprintf(
"%s(%s)",
formatTypeName(rv.Type()),
s,
)
}

// formatComplex formats complex numbers.
func formatComplex(rv reflect.Value, knownType bool) string {
s := fmt.Sprintf("%v", rv.Complex())

if knownType {
return s[1 : len(s)-1] // trim the opening and closing parenthesis
}

return formatTypeName(rv.Type()) + s
}

// formatUintptr formats uintptr values.
func formatUintptr(rv reflect.Value, knownType bool) string {
s := formatPointerHex(rv.Interface(), false)
s := formatPointerHex(rv.Uint(), false)

if knownType {
return s
Expand All @@ -51,7 +78,7 @@ func formatUintptr(rv reflect.Value, knownType bool) string {

// formatUnsafePointer formats unsafe.Pointer values.
func formatUnsafePointer(rv reflect.Value, knownType bool) string {
s := formatPointerHex(rv.Interface(), true)
s := formatPointerHex(rv.Pointer(), true)

if knownType {
return s
Expand Down
22 changes: 11 additions & 11 deletions shallow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ var shallowValues = shallow{
Float32: 1.23,
Float64: 1.23,
Uintptr: 0xABCD,
UnsafePointer: unsafe.Pointer(&unsafePointerTarget),
UnsafePointer: unsafe.Pointer(&pointerTarget),
Channel: make(chan string),
Func: func(int, string) (bool, error) {
panic("not implemented")
},
}

var (
unsafePointerTarget int
unsafePointerTargetHex = fmt.Sprintf("0x%x", &unsafePointerTarget)
channelHex = fmt.Sprintf("0x%x", shallowValues.Channel)
funcHex = fmt.Sprintf("0x%x", reflect.ValueOf(shallowValues.Func).Pointer())
pointerTarget int = 123
pointerTargetHex = fmt.Sprintf("0x%x", &pointerTarget)
channelHex = fmt.Sprintf("0x%x", shallowValues.Channel)
funcHex = fmt.Sprintf("0x%x", reflect.ValueOf(shallowValues.Func).Pointer())
)

// This test verifies the formatting of "shallow" values.
Expand All @@ -84,10 +84,10 @@ func TestPrinter_ShallowValues(t *testing.T) {
test(t, "uint64", shallowValues.Uint64, "uint64(100)")
test(t, "complex64", shallowValues.Complex64, "complex64(100+5i)")
test(t, "complex128", shallowValues.Complex128, "complex128(100+5i)")
test(t, "float32", shallowValues.Float32, "float32(1.23)")
test(t, "float32", shallowValues.Float32, "float32(1.2300000190734863)")
test(t, "float64", shallowValues.Float64, "float64(1.23)")
test(t, "uintptr", shallowValues.Uintptr, "uintptr(0xabcd)")
test(t, "unsafe.Pointer", shallowValues.UnsafePointer, "unsafe.Pointer("+unsafePointerTargetHex+")")
test(t, "unsafe.Pointer", shallowValues.UnsafePointer, "unsafe.Pointer("+pointerTargetHex+")")
test(t, "channel", shallowValues.Channel, "(chan string)("+channelHex+")")
test(t, "func", shallowValues.Func, "(func(int, string) (bool, error))("+funcHex+")")
}
Expand Down Expand Up @@ -116,10 +116,10 @@ func TestPrinter_ShallowValuesInNamedStruct(t *testing.T) {
" Uint64: 100",
" Complex64: 100+5i",
" Complex128: 100+5i",
" Float32: 1.23",
" Float32: 1.2300000190734863",
" Float64: 1.23",
" Uintptr: 0xabcd",
" UnsafePointer: "+unsafePointerTargetHex,
" UnsafePointer: "+pointerTargetHex,
" Channel: "+channelHex,
" Func: "+funcHex,
"}",
Expand Down Expand Up @@ -175,10 +175,10 @@ func TestPrinter_ShallowValuesInAnonymousStruct(t *testing.T) {
" Uint64: uint64(100)",
" Complex64: complex64(100+5i)",
" Complex128: complex128(100+5i)",
" Float32: float32(1.23)",
" Float32: float32(1.2300000190734863)",
" Float64: float64(1.23)",
" Uintptr: uintptr(0xabcd)",
" UnsafePointer: unsafe.Pointer("+unsafePointerTargetHex+")",
" UnsafePointer: unsafe.Pointer("+pointerTargetHex+")",
" Channel: (chan string)("+channelHex+")",
" Func: (func(int, string) (bool, error))("+funcHex+")",
"}",
Expand Down
114 changes: 110 additions & 4 deletions struct_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dapper_test

import "testing"
import (
"testing"
"unsafe"
)

type empty struct{}

Expand All @@ -9,12 +12,41 @@ type named struct {
Iface interface{}
}

type namedWithAnonymousField struct {
type anonymous struct {
Anon struct {
Int int
}
}

type unexported struct {
vString string
vBool bool
vInt int
vInt8 int8
vInt16 int16
vInt32 int32
vInt64 int64
vUint uint
vUint8 uint8
vUint16 uint16
vUint32 uint32
vUint64 uint64
vComplex64 complex64
vComplex128 complex128
vFloat32 float32
vFloat64 float64
vUintptr uintptr
vUnsafePointer unsafe.Pointer
vChannel chan string
vFunc func(int, string) (bool, error)
vIface interface{}
vStruct struct{}
vPtr *int
vSlice []int
vArray [1]int
vMap map[int]int
}

// This test verifies that empty structs are rendered on a single line.
func TestPrinter_EmptyStruct(t *testing.T) {
test(t, "empty struct", empty{}, "dapper_test.empty{}")
Expand Down Expand Up @@ -56,15 +88,89 @@ func TestPrinter_StructFieldTypes(t *testing.T) {
test(
t,
"types are only included for interface fields of anonymous struct inside a named struct",
namedWithAnonymousField{
anonymous{
Anon: struct{ Int int }{
Int: 100,
},
},
"dapper_test.namedWithAnonymousField{",
"dapper_test.anonymous{",
" Anon: {",
" Int: 100",
" }",
"}",
)
}

// This test verifies that all types can be formatted when obtained from
// unexported fields.
//
// This is important because reflect.Value().Interface() panics if called on
// such a value.
func TestPrinter_StructUnexportedFields(t *testing.T) {
test(
t,
"unexported fields can be formatted",
unexported{
vString: shallowValues.String,
vBool: shallowValues.Bool,
vInt: shallowValues.Int,
vInt8: shallowValues.Int8,
vInt16: shallowValues.Int16,
vInt32: shallowValues.Int32,
vInt64: shallowValues.Int64,
vUint: shallowValues.Uint,
vUint8: shallowValues.Uint8,
vUint16: shallowValues.Uint16,
vUint32: shallowValues.Uint32,
vUint64: shallowValues.Uint64,
vComplex64: shallowValues.Complex64,
vComplex128: shallowValues.Complex128,
vFloat32: shallowValues.Float32,
vFloat64: shallowValues.Float64,
vUintptr: shallowValues.Uintptr,
vUnsafePointer: shallowValues.UnsafePointer,
vChannel: shallowValues.Channel,
vFunc: shallowValues.Func,
vIface: 100,
vStruct: struct{}{},
vPtr: &pointerTarget,
vSlice: []int{100},
vArray: [1]int{200},
vMap: map[int]int{300: 400},
},
"dapper_test.unexported{",
` vString: "foo\nbar"`,
" vBool: true",
" vInt: -100",
" vInt8: -100",
" vInt16: -100",
" vInt32: -100",
" vInt64: -100",
" vUint: 100",
" vUint8: 100",
" vUint16: 100",
" vUint32: 100",
" vUint64: 100",
" vComplex64: 100+5i",
" vComplex128: 100+5i",
" vFloat32: 1.2300000190734863",
" vFloat64: 1.23",
" vUintptr: 0xabcd",
" vUnsafePointer: "+pointerTargetHex,
" vChannel: "+channelHex,
" vFunc: "+funcHex,
" vIface: int(100)",
" vStruct: {}",
" vPtr: 123",
" vSlice: {",
" 100",
" }",
" vArray: {",
" 200",
" }",
" vMap: {",
" 300: 400",
" }",
"}",
)
}

0 comments on commit afa9bdf

Please sign in to comment.