Skip to content

Commit 17a3fb5

Browse files
authored
feat: OmitZero and OmitEmpty (#30)
This splits OmitEmpty into OmitEmpty and OmitZero. OmitEmpty will omit anything that is zero as well as a slice and map of length 0. OmitZero only omits the zero type, either by checking the return value of the IsZero method or if that's not implemented by reflect.Value.IsZero. This by default retains the empty map and slice, since initialised but 0-length maps and slices are not zero.
1 parent 4aa10a4 commit 17a3fb5

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

repr.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,24 @@ func Indent(indent string) Option { return func(o *Printer) { o.indent = indent
6363
func NoIndent() Option { return Indent("") }
6464

6565
// OmitEmpty sets whether empty field members should be omitted from output.
66-
func OmitEmpty(omitEmpty bool) Option { return func(o *Printer) { o.omitEmpty = omitEmpty } }
66+
//
67+
// Empty field members are either the zero type, or zero-length maps and slices.
68+
func OmitEmpty(omitEmpty bool) Option {
69+
return func(o *Printer) {
70+
o.omitEmpty = omitEmpty
71+
}
72+
}
73+
74+
// OmitZero sets whether zero field members should be omitted from output.
75+
//
76+
// Field members are considered zero if they have an IsZero method that returns
77+
// true, or if [reflect.Value.IsZero] returns true. Empty maps and slices are
78+
// not zero.
79+
func OmitZero(omitZero bool) Option {
80+
return func(o *Printer) {
81+
o.omitZero = omitZero
82+
}
83+
}
6784

6885
// ExplicitTypes adds explicit typing to slice and map struct values that would normally be inferred by Go.
6986
func ExplicitTypes(ok bool) Option { return func(o *Printer) { o.explicitTypes = true } }
@@ -95,6 +112,7 @@ func AlwaysIncludeType() Option { return func(o *Printer) { o.alwaysIncludeType
95112
type Printer struct {
96113
indent string
97114
omitEmpty bool
115+
omitZero bool
98116
ignoreGoStringer bool
99117
ignorePrivate bool
100118
alwaysIncludeType bool
@@ -110,6 +128,7 @@ func New(w io.Writer, options ...Option) *Printer {
110128
w: w,
111129
indent: " ",
112130
omitEmpty: true,
131+
omitZero: true,
113132
exclude: map[reflect.Type]bool{},
114133
}
115134
for _, option := range options {
@@ -118,6 +137,12 @@ func New(w io.Writer, options ...Option) *Printer {
118137
return p
119138
}
120139

140+
type isZeroer interface {
141+
IsZero() bool
142+
}
143+
144+
var isZeroerType = reflect.TypeFor[isZeroer]()
145+
121146
func (p *Printer) nextIndent(indent string) string {
122147
if p.indent != "" {
123148
return indent + p.indent
@@ -261,11 +286,17 @@ func (p *Printer) reprValue(seen map[reflect.Value]bool, v reflect.Value, indent
261286
if p.ignorePrivate && !f.CanInterface() {
262287
continue
263288
}
289+
290+
if p.omitZero && ((ft.Implements(isZeroerType) && f.Interface().(isZeroer).IsZero()) || f.IsZero()) {
291+
continue
292+
}
293+
264294
if p.omitEmpty && (f.IsZero() ||
265295
ft.Kind() == reflect.Slice && f.Len() == 0 ||
266296
ft.Kind() == reflect.Map && f.Len() == 0) {
267297
continue
268298
}
299+
269300
if previous && p.indent == "" {
270301
fmt.Fprintf(p.w, ", ")
271302
}

repr_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ func TestReprEmptySliceMapFields(t *testing.T) {
5757
equal(t, `struct { S []string; M map[string]string; NZ []string }{NZ: []string{"a", "b"}}`, String(v, OmitEmpty(true)))
5858
}
5959

60+
type zeroTest struct {
61+
i int
62+
}
63+
64+
var _ isZeroer = (*zeroTest)(nil)
65+
66+
func (z zeroTest) IsZero() bool {
67+
return z.i == 100
68+
}
69+
70+
func TestReprZeroSliceMapFields(t *testing.T) {
71+
v := struct {
72+
ZSl []string
73+
Sl []string
74+
ZM map[string]string
75+
M map[string]string
76+
ZI int
77+
I int
78+
ZS string
79+
S string
80+
ZB bool
81+
B bool
82+
ZC zeroTest
83+
C zeroTest
84+
}{[]string{}, []string{"a", "b"}, map[string]string{}, map[string]string{"a": "b"}, 0, 1, "", "a", false, true, zeroTest{i: 100}, zeroTest{i: 10}}
85+
equal(t, `struct { ZSl []string; Sl []string; ZM map[string]string; M map[string]string; ZI int; I int; ZS string; S string; ZB bool; B bool; ZC repr.zeroTest; C repr.zeroTest }{ZSl: []string{}, Sl: []string{"a", "b"}, ZM: map[string]string{}, M: map[string]string{"a": "b"}, I: 1, S: "a", B: true, C: repr.zeroTest{i: 10}}`, String(v, OmitEmpty(false), OmitZero(true)))
86+
}
87+
6088
func TestReprStringArray(t *testing.T) {
6189
equal(t, "[]string{\"a\", \"b\"}", String([]string{"a", "b"}))
6290
}

0 commit comments

Comments
 (0)