From 331add614779f52653ce1c598e1c8be81834cd3c Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:47:08 +0900 Subject: [PATCH] Implement SetOmitEmpty() (#89) --- pp.go | 9 ++++++++ pp_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ printer.go | 16 ++++++++++---- 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/pp.go b/pp.go index bac9958..ec4c9d4 100644 --- a/pp.go +++ b/pp.go @@ -49,6 +49,9 @@ type PrettyPrinter struct { thousandsSeparator bool // This skips unexported fields of structs. exportedOnly bool + + // This skips empty fields of structs. + omitEmpty bool } // New creates a new PrettyPrinter that can be used to pretty print values @@ -66,6 +69,7 @@ func newPrettyPrinter(callerLevel int) *PrettyPrinter { coloringEnabled: true, decimalUint: true, exportedOnly: false, + omitEmpty: false, } } @@ -149,6 +153,11 @@ func (pp *PrettyPrinter) SetExportedOnly(enabled bool) { pp.exportedOnly = enabled } +// SetOmitEmpty makes empty fields in struct not be printed. +func (pp *PrettyPrinter) SetOmitEmpty(enabled bool) { + pp.omitEmpty = enabled +} + func (pp *PrettyPrinter) SetThousandsSeparator(enabled bool) { pp.thousandsSeparator = enabled } diff --git a/pp_test.go b/pp_test.go index aa9e962..6104025 100644 --- a/pp_test.go +++ b/pp_test.go @@ -160,3 +160,66 @@ func TestStructPrintingWithTags(t *testing.T) { } } + +func TestStructPrintingWithOmitEmpty(t *testing.T) { + type Bar struct{ StringField string } + type Foo struct { + StringField string + StringPtr *string + + StructField Bar + StructPtr *Bar + InterfactField interface{} + } + + stringVal := "foo" + + testCases := []struct { + name string + foo Foo + omitIfEmptyOmitted bool + fullOmitted bool + want string + }{ + { + name: "all set", + foo: Foo{ + StringField: "foo", + StringPtr: &stringVal, + StructField: Bar{ + StringField: "baz", + }, + StructPtr: &Bar{ + StringField: "foobar", + }, + InterfactField: &Bar{StringField: "fizzbuzz"}, + }, + want: "pp.Foo{\n StringField: \"foo\",\n StringPtr: &\"foo\",\n StructField: pp.Bar{\n StringField: \"baz\",\n },\n StructPtr: &pp.Bar{\n StringField: \"foobar\",\n },\n InterfactField: &pp.Bar{\n StringField: \"fizzbuzz\",\n },\n}", + }, + { + name: "zero", + foo: Foo{}, + want: "pp.Foo{}", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + output := new(bytes.Buffer) + pp := New() + pp.SetOutput(output) + pp.SetColoringEnabled(false) + pp.SetOmitEmpty(true) + + pp.Print(tc.foo) + + result := output.String() + + if result != tc.want { + t.Errorf("result differ, want: %q, got: %q", tc.want, result) + } + }) + } + +} diff --git a/printer.go b/printer.go index 13e0a0a..ef3c889 100644 --- a/printer.go +++ b/printer.go @@ -22,10 +22,10 @@ const ( ) func (pp *PrettyPrinter) format(object interface{}) string { - return newPrinter(object, &pp.currentScheme, pp.maxDepth, pp.coloringEnabled, pp.decimalUint, pp.exportedOnly, pp.thousandsSeparator).String() + return newPrinter(object, &pp.currentScheme, pp.maxDepth, pp.coloringEnabled, pp.decimalUint, pp.exportedOnly, pp.thousandsSeparator, pp.omitEmpty).String() } -func newPrinter(object interface{}, currentScheme *ColorScheme, maxDepth int, coloringEnabled bool, decimalUint bool, exportedOnly bool, thousandsSeparator bool) *printer { +func newPrinter(object interface{}, currentScheme *ColorScheme, maxDepth int, coloringEnabled bool, decimalUint bool, exportedOnly bool, thousandsSeparator bool, omitEmpty bool) *printer { buffer := bytes.NewBufferString("") tw := new(tabwriter.Writer) tw.Init(buffer, indentWidth, 0, 1, ' ', 0) @@ -42,6 +42,7 @@ func newPrinter(object interface{}, currentScheme *ColorScheme, maxDepth int, co decimalUint: decimalUint, exportedOnly: exportedOnly, thousandsSeparator: thousandsSeparator, + omitEmpty: omitEmpty, } if thousandsSeparator { @@ -63,6 +64,7 @@ type printer struct { decimalUint bool exportedOnly bool thousandsSeparator bool + omitEmpty bool localizedPrinter *message.Printer } @@ -212,7 +214,13 @@ func (p *printer) printStruct() { if p.exportedOnly && field.PkgPath != "" { continue } - // ignore fields if zero value, or explicitly set + + // ignore empty fields if needed + if p.omitEmpty && valueIsZero(value) { + continue + } + + // ignore fields with struct tags if zero value, or explicitly set if tag := field.Tag.Get("pp"); tag != "" { parts := strings.Split(tag, ",") if len(parts) == 2 && parts[1] == "omitempty" && valueIsZero(value) { @@ -469,7 +477,7 @@ func (p *printer) colorize(text string, color uint16) string { } func (p *printer) format(object interface{}) string { - pp := newPrinter(object, p.currentScheme, p.maxDepth, p.coloringEnabled, p.decimalUint, p.exportedOnly, p.thousandsSeparator) + pp := newPrinter(object, p.currentScheme, p.maxDepth, p.coloringEnabled, p.decimalUint, p.exportedOnly, p.thousandsSeparator, false) pp.depth = p.depth pp.visited = p.visited if value, ok := object.(reflect.Value); ok {