Skip to content

Commit

Permalink
✨ feat(dump): support skip nil field dump on map, struct by option Sk…
Browse files Browse the repository at this point in the history
…ipNilField

close #41
  • Loading branch information
inhere committed Dec 12, 2022
1 parent bc0a0b8 commit a55c5ca
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 9 deletions.
34 changes: 34 additions & 0 deletions dump/README.md
Expand Up @@ -61,6 +61,40 @@ You will see:

![](_examples/preview-nested-struct.png)

### Options for dump

```go
// Options for dumper
type Options struct {
// Output the output writer
Output io.Writer
// NoType don't show data type TODO
NoType bool
// NoColor don't with color
NoColor bool
// IndentLen width. default is 2
IndentLen int
// IndentChar default is one space
IndentChar byte
// MaxDepth for nested print
MaxDepth int
// ShowFlag for display caller position
ShowFlag int
// CallerSkip skip for call runtime.Caller()
CallerSkip int
// ColorTheme for print result.
ColorTheme Theme
// SkipNilField value dump on map, struct.
SkipNilField bool
// SkipPrivate field dump on struct.
SkipPrivate bool
// BytesAsString dump handle.
BytesAsString bool
// MoreLenNL array/slice elements length > MoreLenNL, will wrap new line
// MoreLenNL int
}
```

## Functions API

```go
Expand Down
14 changes: 14 additions & 0 deletions dump/dump.go
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"io"
"os"
"reflect"

"github.com/gookit/color"
)
Expand Down Expand Up @@ -130,3 +131,16 @@ func Clear(vs ...any) {
func isUnexported(fieldName string) bool {
return fieldName[0] < 'A' || fieldName[0] > 'Z'
}

func isNilOrInvalid(v reflect.Value) bool {
if !v.IsValid() {
return true
}

switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return v.IsNil()
default:
return false
}
}
22 changes: 22 additions & 0 deletions dump/dump_test.go
Expand Up @@ -308,6 +308,28 @@ func TestStruct_ptrField(t *testing.T) {
color.Infoln("\nUse fmt.Println:")
fmt.Println(opt)
fmt.Println("---------------------------------------------------------------")

opt = &userOpts{
Str: &astr,
}

Println(opt)
/* Output:
PRINT AT github.com/gookit/goutil/dump.TestStruct_ptrField(dump_test.go:316)
&dump.userOpts {
Int: *int<nil>,
Str: &string("xyz"), #len=3
},
*/
d := newStd().WithOptions(SkipNilField())
d.Println(opt)
/* Output:
PRINT AT github.com/gookit/goutil/dump.TestStruct_ptrField(dump_test.go:318)
&dump.userOpts {
Str: &string("xyz"), #len=3
},
*/
}

func TestFormat(t *testing.T) {
Expand Down
18 changes: 10 additions & 8 deletions dump/dumper.go
Expand Up @@ -274,16 +274,17 @@ func (d *Dumper) printRValue(t reflect.Type, v reflect.Value) {

fldNum := v.NumField()
for i := 0; i < fldNum; i++ {
fv := v.Field(i)
d.advance(1)

fName := t.Field(i).Name
if d.SkipPrivate && isUnexported(fName) {
continue
}

// if d.SkipNilField { // TODO
// }
fv := v.Field(i)
if d.SkipNilField && isNilOrInvalid(fv) {
continue
}

d.advance(1)

// print field name
d.indentPrint(d.ColorTheme.field(fName), ": ")
Expand All @@ -303,10 +304,11 @@ func (d *Dumper) printRValue(t reflect.Type, v reflect.Value) {

for _, key := range v.MapKeys() {
mv := v.MapIndex(key)
d.advance(1)
if d.SkipNilField && isNilOrInvalid(mv) {
continue
}

// if d.SkipNilField { // TODO
// }
d.advance(1)

// print key name
if !key.CanInterface() {
Expand Down
5 changes: 4 additions & 1 deletion dump/dumper_test.go
Expand Up @@ -204,6 +204,9 @@ func TestDumper_AccessCantExportedField1(t *testing.T) {
}

Println(myS1)

d := newStd().WithOptions(SkipPrivate())
d.Println(myS1)
}

// ------------------------- map -------------------------
Expand Down Expand Up @@ -355,7 +358,7 @@ var (
)

func TestDump_Struct(t *testing.T) {

P(user)
}

func TestStruct_WithNested(t *testing.T) {
Expand Down
32 changes: 32 additions & 0 deletions dump/options_test.go
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"testing"

"github.com/gookit/color"
"github.com/gookit/goutil/testutil/assert"
)

Expand All @@ -25,7 +26,38 @@ func TestSkipPrivate(t *testing.T) {
assert.NotContains(t, str, "id: string(\"ab12345\")")
}

// see https://github.com/gookit/goutil/issues/41
func TestSkipNilField(t *testing.T) {
buf := newBuffer()
dumper := newStd().WithOptions(WithoutOutput(buf), WithoutPosition(), WithoutColor())
assert.False(t, dumper.SkipNilField)

mp := map[string]any{
"name": "inhere",
"age": nil,
}

dumper.Println(mp)
str := buf.String()
fmt.Print("Default: \n", str)
assert.StrContains(t, str, `"age": nil`)
buf.Reset()

dumper.WithOptions(SkipNilField())
assert.True(t, dumper.SkipNilField)
dumper.Println(mp)

str = buf.String()
fmt.Print("SkipNilField: \n", str)
assert.NotContains(t, str, `"age": nil`)
}

func TestWithoutColor(t *testing.T) {
ol := color.ForceColor()
defer func() {
color.ForceSetColorLevel(ol)
}()

buf := newBuffer()
dumper := newStd().WithOptions(WithoutOutput(buf), WithoutPosition(), WithCallerSkip(2))

Expand Down

0 comments on commit a55c5ca

Please sign in to comment.