Skip to content

Commit

Permalink
a valid JSON byte slice, when pretty printed, will be indented to enh…
Browse files Browse the repository at this point in the history
…ance readability.
  • Loading branch information
adamluzsi committed Jun 19, 2023
1 parent b1734ef commit 48ee9c5
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
33 changes: 20 additions & 13 deletions pp/Format.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pp

import (
"bytes"
"encoding/json"
"fmt"
"github.com/adamluzsi/testcase/internal/reflects"
"io"
Expand All @@ -17,6 +18,12 @@ func Format(v any) string {
return formatter{}.Format(v)
}

var (
typeByteSlice = reflect.TypeOf((*[]byte)(nil)).Elem()
typeTimeDuration = reflect.TypeOf((*time.Duration)(nil)).Elem()
typeTimeTime = reflect.TypeOf((*time.Time)(nil)).Elem()
)

type formatter struct{}

func (f formatter) Format(v any) string {
Expand All @@ -36,11 +43,6 @@ type visitor struct {
stack int
}

var (
typeTimeDuration = reflect.TypeOf((*time.Duration)(nil)).Elem()
typeTimeTime = reflect.TypeOf((*time.Time)(nil)).Elem()
)

func (v *visitor) Visit(w io.Writer, rv reflect.Value, depth int) {
defer debugRecover()
td, ok := v.recursionGuard(w, rv)
Expand Down Expand Up @@ -88,10 +90,10 @@ func (v *visitor) Visit(w io.Writer, rv reflect.Value, depth int) {

switch rv.Kind() {
case reflect.Array, reflect.Slice:
if v.tryByteSlice(w, rv) {
if v.tryNilSlice(w, rv) {
return
}
if v.tryNilSlice(w, rv) {
if v.tryByteSlice(w, rv, depth) {
return
}

Expand Down Expand Up @@ -259,12 +261,12 @@ func (v *visitor) visitStructure(w io.Writer, rv reflect.Value, depth int) {

func (v *visitor) newLine(w io.Writer, depth int) {
_, _ = w.Write([]byte("\n"))
v.indent(w, depth)
_, _ = w.Write([]byte(v.indent(depth)))
}

func (v *visitor) indent(w io.Writer, depth int) {
func (v *visitor) indent(depth int) string {
const defaultIndent = "\t"
_, _ = w.Write([]byte(strings.Repeat(defaultIndent, depth)))
return strings.Repeat(defaultIndent, depth)
}

func (v *visitor) sortMapKeys(keys []reflect.Value) {
Expand All @@ -288,9 +290,7 @@ func (v *visitor) sortMapKeys(keys []reflect.Value) {
})
}

var typeByteSlice = reflect.TypeOf([]byte{})

func (v *visitor) tryByteSlice(w io.Writer, rv reflect.Value) bool {
func (v *visitor) tryByteSlice(w io.Writer, rv reflect.Value, depth int) bool {
if !rv.Type().ConvertibleTo(typeByteSlice) {
return false
}
Expand All @@ -300,6 +300,13 @@ func (v *visitor) tryByteSlice(w io.Writer, rv reflect.Value) bool {
return false
}

if json.Valid(data) {
var buf bytes.Buffer
if err := json.Indent(&buf, data, v.indent(depth), "\t"); err == nil {
data = buf.Bytes()
}
}

var (
typeName = v.getTypeName(rv)
quoteChar = "`"
Expand Down
45 changes: 44 additions & 1 deletion pp/Format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,29 @@ func TestFormat(t *testing.T) {
t.Must.Equal(expected, act(t))
})
})

s.And("it has a field which contains a valid JSON", func(s *testcase.Spec) {
type T struct {
V json.RawMessage
}
type C struct {
Foo string `json:"foo"`
}
ent := testcase.Let(s, func(t *testcase.T) T {
bs, err := json.Marshal(C{Foo: "Charlotte"})
t.Must.NoError(err)
return T{V: bs}
})

v.Let(s, func(t *testcase.T) any {
return ent.Get(t)
})

s.Then("it will print an indentet version of it", func(t *testcase.T) {
exp := "pp_test.T{\n\tV: json.RawMessage(`{\n\t\t\"foo\": \"Charlotte\"\n\t}`),\n}"
t.Must.Equal(exp, act(t))
})
})
})

s.When("v is a slice", func(s *testcase.Spec) {
Expand Down Expand Up @@ -201,11 +224,31 @@ func TestFormat(t *testing.T) {
})

s.Then("it will print out as a UTF-8 string", func(t *testcase.T) {
expected := "json.RawMessage(`{\"foo\":\"bar\"}`)"
expected := "json.RawMessage(`{\n\t\"foo\": \"bar\"\n}`)"
t.Log(expected)
t.Must.Equal(expected, act(t))
})
})

s.And("it is a valid JSON", func(s *testcase.Spec) {
type T struct {
Foo string `json:"foo"`
}
ent := testcase.Let(s, func(t *testcase.T) T {
return T{Foo: t.Random.StringN(8)}
})
v.Let(s, func(t *testcase.T) any {
bs, err := json.Marshal(ent.Get(t))
t.Must.NoError(err)
return bs
})

s.Then("it will print an indentet version of it", func(t *testcase.T) {
exp, err := json.MarshalIndent(ent.Get(t), "", "\t")
t.Must.NoError(err)
t.Must.Equal(fmt.Sprintf("[]byte(`%s`)", string(exp)), act(t))
})
})
})

s.And("it implements fmt.Stringer", func(s *testcase.Spec) {
Expand Down

0 comments on commit 48ee9c5

Please sign in to comment.