Skip to content

Commit

Permalink
✨ feat(dump): support dump []byte as string and more new options
Browse files Browse the repository at this point in the history
New options:
 - SkipNilField TODO
 - SkipPrivate
 - BytesAsString

close #74
  • Loading branch information
inhere committed Dec 12, 2022
1 parent 31b8753 commit 025464a
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 81 deletions.
33 changes: 23 additions & 10 deletions dump/dump.go
Expand Up @@ -18,6 +18,9 @@ const (
Fline
)

const defaultSkip = 3
const defaultSkip2 = 2

var (
// valid flag for print caller info
callerFlags = []int{Ffunc, Ffile, Ffname, Fline}
Expand All @@ -34,7 +37,7 @@ var (
}

// std dumper
std = NewDumper(os.Stdout, 3)
std = NewDumper(os.Stdout, defaultSkip)
// no location dumper.
std2 = NewWithOptions(func(opts *Options) {
opts.Output = os.Stdout
Expand Down Expand Up @@ -62,19 +65,13 @@ func (ct Theme) wrap(key string, s string) string {
}

// Std dumper
func Std() *Dumper {
return std
}
func Std() *Dumper { return std }

// Reset std dumper
func Reset() {
std = NewDumper(os.Stdout, 3)
}
func Reset() { std = NewDumper(os.Stdout, 3) }

// Config std dumper
func Config(fn func(opts *Options)) {
std.WithOptions(fn)
}
func Config(fns ...OptionFunc) { std.WithOptions(fns...) }

// V like fmt.Println, but the output is clearer and more beautiful
func V(vs ...any) {
Expand All @@ -101,6 +98,17 @@ func Fprint(w io.Writer, vs ...any) {
std.Fprint(w, vs...)
}

// Std2 dumper
func Std2() *Dumper { return std2 }

// Reset2 reset std2 dumper
func Reset2() {
std2 = NewWithOptions(func(opts *Options) {
opts.Output = os.Stdout
opts.ShowFlag = Fnopos
})
}

// Format like fmt.Println, but the output is clearer and more beautiful
func Format(vs ...any) string {
w := &bytes.Buffer{}
Expand All @@ -118,3 +126,8 @@ func NoLoc(vs ...any) {
func Clear(vs ...any) {
std2.Println(vs...)
}

// is unexported field name on struct
func isUnexported(fieldName string) bool {
return fieldName[0] < 'A' || fieldName[0] > 'Z'
}
26 changes: 26 additions & 0 deletions dump/dump_test.go
Expand Up @@ -67,6 +67,32 @@ func TestStd(t *testing.T) {
assert.Eq(t, Std().IndentLen, 2)
}

func TestStd2(t *testing.T) {
assert.Eq(t, Std2().NoColor, false)
assert.Eq(t, Std2().IndentLen, 2)
assert.Eq(t, Fnopos, Std2().ShowFlag)

buf := newBuffer()
Std2().WithOptions(func(opt *Options) {
opt.Output = buf
opt.NoColor = true
})
defer Reset2()

NoLoc(123, "abcd")

str := buf.String()
fmt.Print(str)
assert.StrContains(t, str, "int(123)")
assert.NotContains(t, str, "PRINT")

buf.Reset()
Clear(123, "abcd")
str = buf.String()
assert.StrContains(t, str, "int(123)")
assert.NotContains(t, str, "PRINT")
}

func TestConfig(t *testing.T) {
is := assert.New(t)
buf := new(bytes.Buffer)
Expand Down
97 changes: 32 additions & 65 deletions dump/dumper.go
Expand Up @@ -15,30 +15,6 @@ import (
"github.com/gookit/goutil/strutil"
)

// Options for dump vars
type Options struct {
// Output the output writer
Output io.Writer
// NoType dont 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
// MoreLenNL array/slice elements length > MoreLenNL, will wrap new line
// MoreLenNL int
// CallerSkip skip for call runtime.Caller()
CallerSkip int
// ColorTheme for print result.
ColorTheme Theme
}

// printValue must keep track of already-printed pointer values to avoid
// infinite recursion. refer the pkg: github.com/kr/pretty
type visit struct {
Expand Down Expand Up @@ -73,30 +49,8 @@ func NewDumper(out io.Writer, skip int) *Dumper {
}

// NewWithOptions create
func NewWithOptions(fn func(opts *Options)) *Dumper {
d := NewDumper(os.Stdout, 3)
fn(d.Options)
return d
}

// NewDefaultOptions create.
func NewDefaultOptions(out io.Writer, skip int) *Options {
if out == nil {
out = os.Stdout
}

return &Options{
Output: out,
// ---
MaxDepth: 5,
ShowFlag: Ffunc | Ffname | Fline,
// MoreLenNL: 8,
// ---
IndentLen: 2,
IndentChar: ' ',
CallerSkip: skip,
ColorTheme: defaultTheme,
}
func NewWithOptions(fns ...OptionFunc) *Dumper {
return NewDumper(os.Stdout, defaultSkip).WithOptions(fns...)
}

// WithSkip for dumper
Expand All @@ -112,32 +66,28 @@ func (d *Dumper) WithoutColor() *Dumper {
}

// WithOptions for dumper
func (d *Dumper) WithOptions(fn func(opts *Options)) *Dumper {
fn(d.Options)
func (d *Dumper) WithOptions(fns ...OptionFunc) *Dumper {
for _, fn := range fns {
fn(d.Options)
}
return d
}

// ResetOptions for dumper
func (d *Dumper) ResetOptions() {
d.curDepth = 0
d.visited = make(map[visit]int)
d.Options = NewDefaultOptions(os.Stdout, 2)
d.Options = NewDefaultOptions(os.Stdout, d.CallerSkip)
}

// Dump vars
func (d *Dumper) Dump(vs ...any) {
d.dump(vs...)
}
func (d *Dumper) Dump(vs ...any) { d.dump(vs...) }

// Print vars. alias of Dump()
func (d *Dumper) Print(vs ...any) {
d.dump(vs...)
}
func (d *Dumper) Print(vs ...any) { d.dump(vs...) }

// Println vars. alias of Dump()
func (d *Dumper) Println(vs ...any) {
d.dump(vs...)
}
func (d *Dumper) Println(vs ...any) { d.dump(vs...) }

// Fprint print vars to io.Writer
func (d *Dumper) Fprint(w io.Writer, vs ...any) {
Expand Down Expand Up @@ -228,23 +178,29 @@ func (d *Dumper) printOne(v any) {
return
}

if bts, ok := v.([]byte); ok && d.BytesAsString {
strVal := d.ColorTheme.string(string(bts))
lenTip := d.ColorTheme.lenTip("#len=" + strconv.Itoa(len(bts)) + ",cap=" + strconv.Itoa(cap(bts)))
d.printf("[]byte(\"%s\"), %s\n", strVal, lenTip)
return
}

// print reflect value
rv := reflect.ValueOf(v)
d.printRValue(rv.Type(), rv)
}

// print reflect value
func (d *Dumper) printRValue(t reflect.Type, v reflect.Value) {
// var isPtr bool
// if is a ptr, get real type and value
if t.Kind() == reflect.Ptr {
if v.IsNil() {
d.printf("%s<nil>,\n", t.String())
return
}

v = v.Elem()
t = t.Elem()
// add "*" prefix
d.indentPrint("&")
v, t = v.Elem(), t.Elem()
d.indentPrint("&") // add prefix
}

if !v.IsValid() {
Expand Down Expand Up @@ -322,6 +278,14 @@ func (d *Dumper) printRValue(t reflect.Type, v reflect.Value) {
d.advance(1)

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

// if d.SkipNilField { // TODO
// }

// print field name
d.indentPrint(d.ColorTheme.field(fName), ": ")

d.msValue = true
Expand All @@ -341,6 +305,9 @@ func (d *Dumper) printRValue(t reflect.Type, v reflect.Value) {
mv := v.MapIndex(key)
d.advance(1)

// if d.SkipNilField { // TODO
// }

// print key name
if !key.CanInterface() {
// d.printf("<cyan>%s</>: ", key.String())
Expand Down
14 changes: 8 additions & 6 deletions dump/dumper_test.go
Expand Up @@ -16,6 +16,8 @@ import (
// return NewDumper(buf, 2)
// }

const skipInTest = 2

var (
ints1 = []int{1, 2, 3, 4}
ints2 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
Expand All @@ -27,11 +29,11 @@ var (
)

func newStd() *Dumper {
return NewDumper(os.Stdout, 2)
return NewDumper(os.Stdout, skipInTest)
}

func TestNewDefaultOptions(t *testing.T) {
opts := NewDefaultOptions(nil, 2)
opts := NewDefaultOptions(nil, skipInTest)

assert.Eq(t, "<normal>text value</>", opts.ColorTheme.value("text value"))
}
Expand All @@ -53,7 +55,7 @@ func TestDumper_Fprint(t *testing.T) {

func TestDump_Basic(t *testing.T) {
buffer := new(bytes.Buffer)
dumper := NewDumper(buffer, 2)
dumper := NewDumper(buffer, skipInTest)

dumper.Dump(
nil,
Expand Down Expand Up @@ -93,7 +95,7 @@ func TestDump_Basic(t *testing.T) {

func TestDump_Ints(t *testing.T) {
buffer := new(bytes.Buffer)
dumper := NewDumper(buffer, 2)
dumper := NewDumper(buffer, skipInTest)
dumper.WithoutColor()

// assert.Equal(t, 8, dumper.MoreLenNL)
Expand All @@ -119,7 +121,7 @@ func TestDump_Ints(t *testing.T) {

func TestDump_Ptr(t *testing.T) {
buffer := new(bytes.Buffer)
dumper := NewDumper(buffer, 2)
dumper := NewDumper(buffer, skipInTest)
// dumper.WithoutColor()

var s string
Expand Down Expand Up @@ -189,7 +191,7 @@ func TestDumper_AccessCantExportedField(t *testing.T) {

// code from https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields-in-golang
func TestDumper_AccessCantExportedField1(t *testing.T) {
// init an nested struct
// init a nested struct
s1 := st1{st0{2}, 23, "inhere"}
myS1 := struct {
// cannotExport any // ok
Expand Down

0 comments on commit 025464a

Please sign in to comment.