Skip to content

Commit cfd0d4d

Browse files
committed
Implement omision of zeroed-fields with + flag
1 parent 3630c7d commit cfd0d4d

3 files changed

Lines changed: 171 additions & 9 deletions

File tree

example_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@ func Example() {
1010
a, b int
1111
}
1212
var x = []myType{{1, 2}, {3, 4}, {5, 6}}
13-
fmt.Printf("%# v", pretty.Formatter(x))
13+
fmt.Printf("%# v\n", pretty.Formatter(x))
14+
15+
var zeroedFields = []myType{{33, 0}, {a: 0, b: 34}}
16+
// Note the '+' in the format
17+
fmt.Printf("%# +v", pretty.Formatter(zeroedFields))
1418
// output:
1519
// []pretty_test.myType{
1620
// {a:1, b:2},
1721
// {a:3, b:4},
1822
// {a:5, b:6},
1923
// }
24+
// []pretty_test.myType{
25+
// {a:33},
26+
// {b:34},
27+
// }
2028
}

formatter.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ type formatter struct {
2626
// If one of these two flags is not set, or any other verb is used, f will
2727
// format x according to the usual rules of package fmt.
2828
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
29+
//
30+
// If the "+" flag is provided, zero-valued structure fields will be omitted.
31+
// For example:
32+
//
33+
// fmt.Sprintf("%# +v", Formatter(x))
2934
func Formatter(x interface{}) (f fmt.Formatter) {
3035
return formatter{v: reflect.ValueOf(x), quote: true}
3136
}
@@ -55,6 +60,9 @@ func (fo formatter) Format(f fmt.State, c rune) {
5560
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
5661
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
5762
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
63+
if f.Flag('+') {
64+
p.skipZeroFields = true
65+
}
5866
p.printValue(fo.v, true, fo.quote)
5967
w.Flush()
6068
return
@@ -64,9 +72,10 @@ func (fo formatter) Format(f fmt.State, c rune) {
6472

6573
type printer struct {
6674
io.Writer
67-
tw *tabwriter.Writer
68-
visited map[visit]int
69-
depth int
75+
tw *tabwriter.Writer
76+
visited map[visit]int
77+
depth int
78+
skipZeroFields bool
7079
}
7180

7281
func (p *printer) indent() *printer {
@@ -169,20 +178,35 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) {
169178
writeByte(p, '\n')
170179
pp = p.indent()
171180
}
181+
type field struct {
182+
name string
183+
t reflect.Type
184+
value reflect.Value
185+
}
186+
fields := make([]field, 0, v.NumField())
187+
// Collect fields, filtering out zero fields if needed
172188
for i := 0; i < v.NumField(); i++ {
189+
value := getField(v, i)
190+
if p.skipZeroFields && !nonzero(value) {
191+
continue
192+
}
193+
f := t.Field(i)
194+
fields = append(fields, field{f.Name, f.Type, value})
195+
}
196+
for i, field := range fields {
173197
showTypeInStruct := true
174-
if f := t.Field(i); f.Name != "" {
175-
io.WriteString(pp, f.Name)
198+
if field.name != "" {
199+
io.WriteString(pp, field.name)
176200
writeByte(pp, ':')
177201
if expand {
178202
writeByte(pp, '\t')
179203
}
180-
showTypeInStruct = labelType(f.Type)
204+
showTypeInStruct = labelType(field.t)
181205
}
182-
pp.printValue(getField(v, i), showTypeInStruct, true)
206+
pp.printValue(field.value, showTypeInStruct, true)
183207
if expand {
184208
io.WriteString(pp, ",\n")
185-
} else if i < v.NumField()-1 {
209+
} else if i < len(fields)-1 {
186210
io.WriteString(pp, ", ")
187211
}
188212
}

formatter_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,124 @@ var gosyntax = []test{
164164
},
165165
}
166166

167+
var gosyntaxSkipZeroFields = []test{
168+
{nil, `nil`},
169+
{"", `""`},
170+
{"a", `"a"`},
171+
{1, "int(1)"},
172+
{1.0, "float64(1)"},
173+
{[]int(nil), "[]int(nil)"},
174+
{[0]int{}, "[0]int{}"},
175+
{complex(1, 0), "(1+0i)"},
176+
//{make(chan int), "(chan int)(0x1234)"},
177+
{unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long)))},
178+
{func(int) {}, "func(int) {...}"},
179+
{map[string]string{"a": "a", "b": "b"}, "map[string]string{\"a\":\"a\", \"b\":\"b\"}"},
180+
{map[int]int{1: 1}, "map[int]int{1:1}"},
181+
{int32(1), "int32(1)"},
182+
{io.EOF, `&errors.errorString{s:"EOF"}`},
183+
{[]string{"a"}, `[]string{"a"}`},
184+
{
185+
[]string{long},
186+
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
187+
},
188+
{F(5), "pretty.F(5)"},
189+
{
190+
SA{&T{1, 2}, T{3, 4}},
191+
`pretty.SA{
192+
t: &pretty.T{x:1, y:2},
193+
v: pretty.T{x:3, y:4},
194+
}`,
195+
},
196+
{
197+
map[int][]byte{1: {}},
198+
`map[int][]uint8{
199+
1: {},
200+
}`,
201+
},
202+
{
203+
map[int]T{1: {}},
204+
`map[int]pretty.T{
205+
1: {},
206+
}`,
207+
},
208+
{
209+
long,
210+
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`,
211+
},
212+
{
213+
LongStructTypeName{
214+
longFieldName: LongStructTypeName{},
215+
otherLongFieldName: long,
216+
},
217+
`pretty.LongStructTypeName{
218+
otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
219+
}`,
220+
},
221+
{
222+
LongStructTypeName{
223+
longFieldName: long,
224+
otherLongFieldName: "",
225+
},
226+
`pretty.LongStructTypeName{
227+
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
228+
}`,
229+
},
230+
{
231+
LongStructTypeName{
232+
longFieldName: "",
233+
otherLongFieldName: "",
234+
},
235+
`pretty.LongStructTypeName{}`,
236+
},
237+
{
238+
&LongStructTypeName{
239+
longFieldName: &LongStructTypeName{},
240+
otherLongFieldName: (*LongStructTypeName)(nil),
241+
},
242+
`&pretty.LongStructTypeName{
243+
longFieldName: &pretty.LongStructTypeName{},
244+
}`,
245+
},
246+
{
247+
[]LongStructTypeName{
248+
{nil, nil},
249+
{3, 3},
250+
{long, nil},
251+
},
252+
`[]pretty.LongStructTypeName{
253+
{},
254+
{
255+
longFieldName: int(3),
256+
otherLongFieldName: int(3),
257+
},
258+
{
259+
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
260+
},
261+
}`,
262+
},
263+
{
264+
[]interface{}{
265+
LongStructTypeName{nil, nil},
266+
[]byte{1, 2, 3},
267+
T{3, 4},
268+
T{0, 4},
269+
T{3, 0},
270+
LongStructTypeName{long, nil},
271+
},
272+
`[]interface {}{
273+
pretty.LongStructTypeName{},
274+
[]uint8{0x1, 0x2, 0x3},
275+
pretty.T{x:3, y:4},
276+
pretty.T{y:4},
277+
pretty.T{x:3},
278+
pretty.LongStructTypeName{
279+
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
280+
},
281+
}`,
282+
},
283+
}
284+
167285
func TestGoSyntax(t *testing.T) {
168286
for _, tt := range gosyntax {
169287
s := fmt.Sprintf("%# v", Formatter(tt.v))
@@ -176,6 +294,18 @@ func TestGoSyntax(t *testing.T) {
176294
}
177295
}
178296

297+
func TestGoSyntaxSkipZeroFields(t *testing.T) {
298+
for _, tt := range gosyntaxSkipZeroFields {
299+
s := fmt.Sprintf("%# +v", Formatter(tt.v))
300+
if tt.s != s {
301+
t.Errorf("expected %q", tt.s)
302+
t.Errorf("got %q", s)
303+
t.Errorf("expraw\n%s", tt.s)
304+
t.Errorf("gotraw\n%s", s)
305+
}
306+
}
307+
}
308+
179309
type I struct {
180310
i int
181311
R interface{}

0 commit comments

Comments
 (0)