Skip to content

Commit

Permalink
Implement omision of zeroed-fields with + flag
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed Aug 27, 2020
1 parent 3630c7d commit cfd0d4d
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 9 deletions.
10 changes: 9 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ func Example() {
a, b int
}
var x = []myType{{1, 2}, {3, 4}, {5, 6}}
fmt.Printf("%# v", pretty.Formatter(x))
fmt.Printf("%# v\n", pretty.Formatter(x))

var zeroedFields = []myType{{33, 0}, {a: 0, b: 34}}
// Note the '+' in the format
fmt.Printf("%# +v", pretty.Formatter(zeroedFields))
// output:
// []pretty_test.myType{
// {a:1, b:2},
// {a:3, b:4},
// {a:5, b:6},
// }
// []pretty_test.myType{
// {a:33},
// {b:34},
// }
}
40 changes: 32 additions & 8 deletions formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ type formatter struct {
// If one of these two flags is not set, or any other verb is used, f will
// format x according to the usual rules of package fmt.
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
//
// If the "+" flag is provided, zero-valued structure fields will be omitted.
// For example:
//
// fmt.Sprintf("%# +v", Formatter(x))
func Formatter(x interface{}) (f fmt.Formatter) {
return formatter{v: reflect.ValueOf(x), quote: true}
}
Expand Down Expand Up @@ -55,6 +60,9 @@ func (fo formatter) Format(f fmt.State, c rune) {
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
if f.Flag('+') {
p.skipZeroFields = true
}
p.printValue(fo.v, true, fo.quote)
w.Flush()
return
Expand All @@ -64,9 +72,10 @@ func (fo formatter) Format(f fmt.State, c rune) {

type printer struct {
io.Writer
tw *tabwriter.Writer
visited map[visit]int
depth int
tw *tabwriter.Writer
visited map[visit]int
depth int
skipZeroFields bool
}

func (p *printer) indent() *printer {
Expand Down Expand Up @@ -169,20 +178,35 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) {
writeByte(p, '\n')
pp = p.indent()
}
type field struct {
name string
t reflect.Type
value reflect.Value
}
fields := make([]field, 0, v.NumField())
// Collect fields, filtering out zero fields if needed
for i := 0; i < v.NumField(); i++ {
value := getField(v, i)
if p.skipZeroFields && !nonzero(value) {
continue
}
f := t.Field(i)
fields = append(fields, field{f.Name, f.Type, value})
}
for i, field := range fields {
showTypeInStruct := true
if f := t.Field(i); f.Name != "" {
io.WriteString(pp, f.Name)
if field.name != "" {
io.WriteString(pp, field.name)
writeByte(pp, ':')
if expand {
writeByte(pp, '\t')
}
showTypeInStruct = labelType(f.Type)
showTypeInStruct = labelType(field.t)
}
pp.printValue(getField(v, i), showTypeInStruct, true)
pp.printValue(field.value, showTypeInStruct, true)
if expand {
io.WriteString(pp, ",\n")
} else if i < v.NumField()-1 {
} else if i < len(fields)-1 {
io.WriteString(pp, ", ")
}
}
Expand Down
130 changes: 130 additions & 0 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,124 @@ var gosyntax = []test{
},
}

var gosyntaxSkipZeroFields = []test{
{nil, `nil`},
{"", `""`},
{"a", `"a"`},
{1, "int(1)"},
{1.0, "float64(1)"},
{[]int(nil), "[]int(nil)"},
{[0]int{}, "[0]int{}"},
{complex(1, 0), "(1+0i)"},
//{make(chan int), "(chan int)(0x1234)"},
{unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long)))},
{func(int) {}, "func(int) {...}"},
{map[string]string{"a": "a", "b": "b"}, "map[string]string{\"a\":\"a\", \"b\":\"b\"}"},
{map[int]int{1: 1}, "map[int]int{1:1}"},
{int32(1), "int32(1)"},
{io.EOF, `&errors.errorString{s:"EOF"}`},
{[]string{"a"}, `[]string{"a"}`},
{
[]string{long},
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
},
{F(5), "pretty.F(5)"},
{
SA{&T{1, 2}, T{3, 4}},
`pretty.SA{
t: &pretty.T{x:1, y:2},
v: pretty.T{x:3, y:4},
}`,
},
{
map[int][]byte{1: {}},
`map[int][]uint8{
1: {},
}`,
},
{
map[int]T{1: {}},
`map[int]pretty.T{
1: {},
}`,
},
{
long,
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`,
},
{
LongStructTypeName{
longFieldName: LongStructTypeName{},
otherLongFieldName: long,
},
`pretty.LongStructTypeName{
otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
}`,
},
{
LongStructTypeName{
longFieldName: long,
otherLongFieldName: "",
},
`pretty.LongStructTypeName{
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
}`,
},
{
LongStructTypeName{
longFieldName: "",
otherLongFieldName: "",
},
`pretty.LongStructTypeName{}`,
},
{
&LongStructTypeName{
longFieldName: &LongStructTypeName{},
otherLongFieldName: (*LongStructTypeName)(nil),
},
`&pretty.LongStructTypeName{
longFieldName: &pretty.LongStructTypeName{},
}`,
},
{
[]LongStructTypeName{
{nil, nil},
{3, 3},
{long, nil},
},
`[]pretty.LongStructTypeName{
{},
{
longFieldName: int(3),
otherLongFieldName: int(3),
},
{
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
},
}`,
},
{
[]interface{}{
LongStructTypeName{nil, nil},
[]byte{1, 2, 3},
T{3, 4},
T{0, 4},
T{3, 0},
LongStructTypeName{long, nil},
},
`[]interface {}{
pretty.LongStructTypeName{},
[]uint8{0x1, 0x2, 0x3},
pretty.T{x:3, y:4},
pretty.T{y:4},
pretty.T{x:3},
pretty.LongStructTypeName{
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
},
}`,
},
}

func TestGoSyntax(t *testing.T) {
for _, tt := range gosyntax {
s := fmt.Sprintf("%# v", Formatter(tt.v))
Expand All @@ -176,6 +294,18 @@ func TestGoSyntax(t *testing.T) {
}
}

func TestGoSyntaxSkipZeroFields(t *testing.T) {
for _, tt := range gosyntaxSkipZeroFields {
s := fmt.Sprintf("%# +v", Formatter(tt.v))
if tt.s != s {
t.Errorf("expected %q", tt.s)
t.Errorf("got %q", s)
t.Errorf("expraw\n%s", tt.s)
t.Errorf("gotraw\n%s", s)
}
}
}

type I struct {
i int
R interface{}
Expand Down

0 comments on commit cfd0d4d

Please sign in to comment.