+
Skip to content

feat: OmitZero and OmitEmpty #30

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

Merged
merged 1 commit into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion repr.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,24 @@ func Indent(indent string) Option { return func(o *Printer) { o.indent = indent
func NoIndent() Option { return Indent("") }

// OmitEmpty sets whether empty field members should be omitted from output.
func OmitEmpty(omitEmpty bool) Option { return func(o *Printer) { o.omitEmpty = omitEmpty } }
//
// Empty field members are either the zero type, or zero-length maps and slices.
func OmitEmpty(omitEmpty bool) Option {
return func(o *Printer) {
o.omitEmpty = omitEmpty
}
}

// OmitZero sets whether zero field members should be omitted from output.
//
// Field members are considered zero if they have an IsZero method that returns
// true, or if [reflect.Value.IsZero] returns true. Empty maps and slices are
// not zero.
func OmitZero(omitZero bool) Option {
return func(o *Printer) {
o.omitZero = omitZero
}
}

// ExplicitTypes adds explicit typing to slice and map struct values that would normally be inferred by Go.
func ExplicitTypes(ok bool) Option { return func(o *Printer) { o.explicitTypes = true } }
Expand Down Expand Up @@ -95,6 +112,7 @@ func AlwaysIncludeType() Option { return func(o *Printer) { o.alwaysIncludeType
type Printer struct {
indent string
omitEmpty bool
omitZero bool
ignoreGoStringer bool
ignorePrivate bool
alwaysIncludeType bool
Expand All @@ -110,6 +128,7 @@ func New(w io.Writer, options ...Option) *Printer {
w: w,
indent: " ",
omitEmpty: true,
omitZero: true,
exclude: map[reflect.Type]bool{},
}
for _, option := range options {
Expand All @@ -118,6 +137,12 @@ func New(w io.Writer, options ...Option) *Printer {
return p
}

type isZeroer interface {
IsZero() bool
}

var isZeroerType = reflect.TypeFor[isZeroer]()

func (p *Printer) nextIndent(indent string) string {
if p.indent != "" {
return indent + p.indent
Expand Down Expand Up @@ -261,11 +286,17 @@ func (p *Printer) reprValue(seen map[reflect.Value]bool, v reflect.Value, indent
if p.ignorePrivate && !f.CanInterface() {
continue
}

if p.omitZero && ((ft.Implements(isZeroerType) && f.Interface().(isZeroer).IsZero()) || f.IsZero()) {
continue
}

if p.omitEmpty && (f.IsZero() ||
ft.Kind() == reflect.Slice && f.Len() == 0 ||
ft.Kind() == reflect.Map && f.Len() == 0) {
continue
}

if previous && p.indent == "" {
fmt.Fprintf(p.w, ", ")
}
Expand Down
28 changes: 28 additions & 0 deletions repr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ func TestReprEmptySliceMapFields(t *testing.T) {
equal(t, `struct { S []string; M map[string]string; NZ []string }{NZ: []string{"a", "b"}}`, String(v, OmitEmpty(true)))
}

type zeroTest struct {
i int
}

var _ isZeroer = (*zeroTest)(nil)

func (z zeroTest) IsZero() bool {
return z.i == 100
}

func TestReprZeroSliceMapFields(t *testing.T) {
v := 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 zeroTest
C zeroTest
}{[]string{}, []string{"a", "b"}, map[string]string{}, map[string]string{"a": "b"}, 0, 1, "", "a", false, true, zeroTest{i: 100}, zeroTest{i: 10}}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, I think this would be easier to read with a concrete type declared in the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah probably 😅.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only issue with that is I can't declare functions on those, can I?

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)))
}

func TestReprStringArray(t *testing.T) {
equal(t, "[]string{\"a\", \"b\"}", String([]string{"a", "b"}))
}
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载