Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement omision of zeroed-fields with + flag #69

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -196,20 +205,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 @@ -214,6 +214,124 @@ func (g *PanicGoString) GoString() string {
panic(g.s)
}

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 @@ -226,6 +344,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