diff --git a/binary_test.go b/binary_test.go
index 4809ce6..3929b7a 100644
--- a/binary_test.go
+++ b/binary_test.go
@@ -1,12 +1,12 @@
package render_test
import (
+ "encoding"
"errors"
"testing"
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
type mockBinaryMarshaler struct {
@@ -14,6 +14,8 @@ type mockBinaryMarshaler struct {
err error
}
+var _ encoding.BinaryMarshaler = (*mockBinaryMarshaler)(nil)
+
func (mbm *mockBinaryMarshaler) MarshalBinary() ([]byte, error) {
return mbm.data, mbm.err
}
@@ -57,16 +59,12 @@ func TestBinary_Render(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &render.Binary{}
-
- var err error
- var got string
w := &mockWriter{WriteErr: tt.writeErr}
- err = b.Render(w, tt.value)
- got = w.String()
+ err := b.Render(w, tt.value)
+ got := w.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -74,7 +72,7 @@ func TestBinary_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/format.go b/format.go
index febe1f8..96efc19 100644
--- a/format.go
+++ b/format.go
@@ -1,17 +1,22 @@
package render
import (
+ "fmt"
"io"
)
+var ErrUnsupportedFormat = fmt.Errorf("%w: unsupported format", Err)
+
// FormatRenderer is a renderer that delegates rendering to another renderer
// based on a format value.
type FormatRenderer struct {
+ // Renderers is a map of format names to renderers. When Render is called,
+ // the format is used to look up the renderer to use.
Renderers map[string]Renderer
}
// Render renders a value to an io.Writer using the specified format. If the
-// format is not supported, ErrCannotRender is returned.
+// format is not supported, ErrUnsupportedFormat is returned.
//
// If the format is supported, but the value cannot be rendered to the format,
// the error returned by the renderer is returned. In most cases this will be
@@ -23,5 +28,5 @@ func (r *FormatRenderer) Render(w io.Writer, format string, v any) error {
return renderer.Render(w, v)
}
- return ErrCannotRender
+ return ErrUnsupportedFormat
}
diff --git a/format_test.go b/format_test.go
index 4ceaea0..de94946 100644
--- a/format_test.go
+++ b/format_test.go
@@ -7,7 +7,6 @@ import (
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestFormatRenderer_Render(t *testing.T) {
@@ -46,7 +45,7 @@ func TestFormatRenderer_Render(t *testing.T) {
renderers: map[string]render.Renderer{},
format: "unknown",
value: struct{}{},
- wantErrIs: []error{render.Err, render.ErrCannotRender},
+ wantErrIs: []error{render.Err, render.ErrUnsupportedFormat},
},
}
for _, tt := range tests {
@@ -54,13 +53,12 @@ func TestFormatRenderer_Render(t *testing.T) {
fr := &render.FormatRenderer{
Renderers: tt.renderers,
}
-
var buf bytes.Buffer
+
err := fr.Render(&buf, tt.format, tt.value)
got := buf.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -68,7 +66,7 @@ func TestFormatRenderer_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/json_test.go b/json_test.go
index 68af345..bd78e0a 100644
--- a/json_test.go
+++ b/json_test.go
@@ -2,6 +2,8 @@ package render_test
import (
"bytes"
+ "encoding/json"
+ "errors"
"testing"
"github.com/jimeh/go-render"
@@ -9,6 +11,17 @@ import (
"github.com/stretchr/testify/require"
)
+type mockJSONMarshaler struct {
+ data []byte
+ err error
+}
+
+var _ json.Marshaler = (*mockJSONMarshaler)(nil)
+
+func (mjm *mockJSONMarshaler) MarshalJSON() ([]byte, error) {
+ return mjm.data, mjm.err
+}
+
func TestJSON_Render(t *testing.T) {
tests := []struct {
name string
@@ -29,18 +42,35 @@ func TestJSON_Render(t *testing.T) {
{
name: "simple object with pretty",
pretty: true,
- indent: " ",
value: map[string]int{"age": 30},
want: "{\n \"age\": 30\n}\n",
},
{
- name: "with prefix and indent",
+ name: "pretty with prefix and indent",
pretty: true,
prefix: "// ",
indent: "\t",
value: map[string]int{"age": 30},
want: "{\n// \t\"age\": 30\n// }\n",
},
+ {
+ name: "prefix and indent without pretty",
+ pretty: false,
+ prefix: "// ",
+ indent: "\t",
+ value: map[string]int{"age": 30},
+ want: "{\"age\":30}\n",
+ },
+ {
+ name: "implements json.Marshaler",
+ value: &mockJSONMarshaler{data: []byte(`{"age":30}`)},
+ want: "{\"age\":30}\n",
+ },
+ {
+ name: "error from json.Marshaler",
+ value: &mockJSONMarshaler{err: errors.New("marshal error!!1")},
+ wantErrIs: []error{render.Err},
+ },
{
name: "invalid value",
pretty: false,
@@ -49,7 +79,6 @@ func TestJSON_Render(t *testing.T) {
wantErrIs: []error{render.Err},
},
}
-
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := &render.JSON{
@@ -57,9 +86,10 @@ func TestJSON_Render(t *testing.T) {
Prefix: tt.prefix,
Indent: tt.indent,
}
-
var buf bytes.Buffer
+
err := j.Render(&buf, tt.value)
+ got := buf.String()
if tt.wantErr != "" {
require.Error(t, err)
@@ -71,7 +101,6 @@ func TestJSON_Render(t *testing.T) {
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
require.NoError(t, err)
- got := buf.String()
assert.Equal(t, tt.want, got)
}
})
diff --git a/multi_test.go b/multi_test.go
index 6774d9f..5907802 100644
--- a/multi_test.go
+++ b/multi_test.go
@@ -7,7 +7,6 @@ import (
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestMultiRenderer_Render(t *testing.T) {
@@ -88,13 +87,12 @@ func TestMultiRenderer_Render(t *testing.T) {
mr := &render.MultiRenderer{
Renderers: tt.renderers,
}
-
var buf bytes.Buffer
+
err := mr.Render(&buf, tt.value)
got := buf.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -102,7 +100,7 @@ func TestMultiRenderer_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/render.go b/render.go
index c0ee7fc..42039d0 100644
--- a/render.go
+++ b/render.go
@@ -48,21 +48,22 @@ var (
Renderers: []Renderer{DefaultStringer, DefaultWriterTo},
}
- // DefaultBinaryMarshaler is the default binary marshaler renderer. It
+ // DefaultBinary is the default binary marshaler renderer. It
// renders values using the encoding.BinaryMarshaler interface.
- DefaultBinaryMarshaler = &Binary{}
+ DefaultBinary = &Binary{}
// DefaultRenderer is the default renderer, used by the package level Render
- // function. It supports the "json", "xml", "yaml", "text", "binary"
- // formats.
+ // function.
DefaultRenderer = &FormatRenderer{map[string]Renderer{
+ "bin": DefaultBinary,
+ "binary": DefaultBinary,
"json": DefaultJSON,
+ "plain": DefaultText,
+ "text": DefaultText,
+ "txt": DefaultText,
"xml": DefaultXML,
"yaml": DefaultYAML,
"yml": DefaultYAML,
- "text": DefaultText,
- "binary": DefaultBinaryMarshaler,
- "bin": DefaultBinaryMarshaler,
}}
)
@@ -81,10 +82,13 @@ var (
// - "text": Renders values using the fmt.Stringer and io.WriterTo interfaces.
// This means a value must implement either the fmt.Stringer or io.WriterTo
// interfaces to be rendered.
+// - "txt": Alias for "text".
+// - "plain": Alias for "text".
// - "binary": Renders values using the encoding.BinaryMarshaler interface.
// - "bin": Alias for "binary".
//
-// If the format is not supported, a ErrCannotRender error will be returned.
+// If the format is not supported, a ErrUnsupportedFormat error will be
+// returned.
func Render(w io.Writer, format string, v any) error {
return DefaultRenderer.Render(w, format, v)
}
diff --git a/render_test.go b/render_test.go
index c9991bf..c5f0f9c 100644
--- a/render_test.go
+++ b/render_test.go
@@ -2,7 +2,13 @@ package render_test
import (
"bytes"
+ "encoding/xml"
+ "errors"
"io"
+ "testing"
+
+ "github.com/jimeh/go-render"
+ "github.com/stretchr/testify/assert"
)
type mockWriter struct {
@@ -10,6 +16,8 @@ type mockWriter struct {
buf bytes.Buffer
}
+var _ io.Writer = (*mockWriter)(nil)
+
func (mw *mockWriter) Write(p []byte) (n int, err error) {
if mw.WriteErr != nil {
return 0, mw.WriteErr
@@ -27,6 +35,8 @@ type mockRenderer struct {
err error
}
+var _ render.Renderer = (*mockRenderer)(nil)
+
func (m *mockRenderer) Render(w io.Writer, _ any) error {
_, err := w.Write([]byte(m.output))
@@ -36,3 +46,478 @@ func (m *mockRenderer) Render(w io.Writer, _ any) error {
return err
}
+
+func TestDefaultJSON(t *testing.T) {
+ assert.Equal(t, &render.JSON{Pretty: true}, render.DefaultJSON)
+}
+
+func TestDefaultXML(t *testing.T) {
+ assert.Equal(t, &render.XML{Pretty: true}, render.DefaultXML)
+}
+
+func TestDefaultYAML(t *testing.T) {
+ assert.Equal(t, &render.YAML{Indent: 2}, render.DefaultYAML)
+}
+
+func TestDefaultWriterTo(t *testing.T) {
+ assert.Equal(t, &render.WriterTo{}, render.DefaultWriterTo)
+}
+
+func TestDefaultStringer(t *testing.T) {
+ assert.Equal(t, &render.Stringer{}, render.DefaultStringer)
+}
+
+func TestDefaultText(t *testing.T) {
+ want := &render.MultiRenderer{
+ Renderers: []render.Renderer{
+ &render.Stringer{},
+ &render.WriterTo{},
+ },
+ }
+
+ assert.Equal(t, want, render.DefaultText)
+}
+
+func TestDefaultBinary(t *testing.T) {
+ assert.Equal(t, &render.Binary{}, render.DefaultBinary)
+}
+
+func TestDefaultRenderer(t *testing.T) {
+ want := &render.FormatRenderer{
+ Renderers: map[string]render.Renderer{
+ "bin": render.DefaultBinary,
+ "binary": render.DefaultBinary,
+ "json": render.DefaultJSON,
+ "plain": render.DefaultText,
+ "text": render.DefaultText,
+ "txt": render.DefaultText,
+ "xml": render.DefaultXML,
+ "yaml": render.DefaultYAML,
+ "yml": render.DefaultYAML,
+ },
+ }
+
+ assert.Equal(t, want, render.DefaultRenderer)
+}
+
+func TestRender(t *testing.T) {
+ tests := []struct {
+ name string
+ writeErr error
+ format string
+ value any
+ want string
+ wantErr string
+ wantErrIs []error
+ wantPanic string
+ }{
+ // "bin" format.
+ {
+ name: "bin format with binary marshaler",
+ format: "bin",
+ value: &mockBinaryMarshaler{data: []byte("test string")},
+ want: "test string",
+ },
+ {
+ name: "bin format without binary marshaler",
+ format: "bin",
+ value: struct{}{},
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ {
+ name: "bin format with error marshaling",
+ format: "bin",
+ value: &mockBinaryMarshaler{
+ data: []byte("test string"),
+ err: errors.New("marshal error!!1"),
+ },
+ wantErr: "render: marshal error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "bin format with error writing to writer",
+ format: "bin",
+ writeErr: errors.New("write error!!1"),
+ value: &mockBinaryMarshaler{data: []byte("test string")},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "bin format with invalid type",
+ format: "bin",
+ value: make(chan int),
+ wantErr: "render: cannot render",
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ // "binary" format.
+ {
+ name: "binary format with binary marshaler",
+ format: "binary",
+ value: &mockBinaryMarshaler{data: []byte("test string")},
+ want: "test string",
+ },
+ {
+ name: "binary format without binary marshaler",
+ format: "binary",
+ value: struct{}{},
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ {
+ name: "binary format with error marshaling",
+ format: "binary",
+ value: &mockBinaryMarshaler{
+ data: []byte("test string"),
+ err: errors.New("marshal error!!1"),
+ },
+ wantErr: "render: marshal error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "binary format with error writing to writer",
+ format: "binary",
+ writeErr: errors.New("write error!!1"),
+ value: &mockBinaryMarshaler{data: []byte("test string")},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "binary format with invalid type",
+ format: "binary",
+ value: make(chan int),
+ wantErr: "render: cannot render",
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ // "json" format.
+ {
+ name: "json format",
+ format: "json",
+ value: map[string]int{"age": 30},
+ want: "{\n \"age\": 30\n}\n",
+ },
+ {
+ name: "json format with json marshaler",
+ format: "json",
+ value: &mockJSONMarshaler{data: []byte(`{"age":30}`)},
+ want: "{\n \"age\": 30\n}\n",
+ },
+ {
+ name: "json format with error from json marshaler",
+ format: "json",
+ value: &mockJSONMarshaler{err: errors.New("marshal error!!1")},
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "json format with error writing to writer",
+ format: "json",
+ writeErr: errors.New("write error!!1"),
+ value: map[string]int{"age": 30},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "json format with invalid type",
+ format: "json",
+ value: make(chan int),
+ wantErr: "render: json: unsupported type: chan int",
+ wantErrIs: []error{render.Err},
+ },
+ // "plain" format.
+ {
+ name: "plain format with fmt.Stringer",
+ format: "plain",
+ value: &mockStringer{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "plain format with io.WriterTo",
+ format: "plain",
+ value: &mockWriterTo{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "plain format without fmt.Stringer or io.WriterTo",
+ format: "plain",
+ value: struct{}{},
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ {
+ name: "plain format with error writing to writer",
+ format: "plain",
+ writeErr: errors.New("write error!!1"),
+ value: &mockStringer{value: "test string"},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "plain format with error from io.WriterTo",
+ format: "plain",
+ value: &mockWriterTo{
+ value: "test string",
+ err: errors.New("WriteTo error!!1"),
+ },
+ wantErr: "render: WriteTo error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "plain format with invalid type",
+ format: "plain",
+ value: make(chan int),
+ wantErr: "render: cannot render",
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ // "text" format.
+ {
+ name: "text format with fmt.Stringer",
+ format: "text",
+ value: &mockStringer{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "text format with io.WriterTo",
+ format: "text",
+ value: &mockWriterTo{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "text format without fmt.Stringer or io.WriterTo",
+ format: "text",
+ value: struct{}{},
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ {
+ name: "text format with error writing to writer",
+ format: "text",
+ writeErr: errors.New("write error!!1"),
+ value: &mockStringer{value: "test string"},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "text format with error from io.WriterTo",
+ format: "text",
+ value: &mockWriterTo{
+ value: "test string",
+ err: errors.New("WriteTo error!!1"),
+ },
+ wantErr: "render: WriteTo error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "text format with invalid type",
+ format: "text",
+ value: make(chan int),
+ wantErr: "render: cannot render",
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ // "txt" format.
+ {
+ name: "txt format with fmt.Stringer",
+ format: "txt",
+ value: &mockStringer{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "txt format with io.WriterTo",
+ format: "txt",
+ value: &mockWriterTo{value: "test string"},
+ want: "test string",
+ },
+ {
+ name: "txt format without fmt.Stringer or io.WriterTo",
+ format: "txt",
+ value: struct{}{},
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ {
+ name: "txt format with error writing to writer",
+ format: "txt",
+ writeErr: errors.New("write error!!1"),
+ value: &mockStringer{value: "test string"},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "txt format with error from io.WriterTo",
+ format: "txt",
+ value: &mockWriterTo{
+ value: "test string",
+ err: errors.New("WriteTo error!!1"),
+ },
+ wantErr: "render: WriteTo error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "txt format with invalid type",
+ format: "txt",
+ value: make(chan int),
+ wantErr: "render: cannot render",
+ wantErrIs: []error{render.Err, render.ErrCannotRender},
+ },
+ // "xml" format.
+ {
+ name: "xml format",
+ format: "xml",
+ value: struct {
+ XMLName xml.Name `xml:"user"`
+ Age int `xml:"age"`
+ }{Age: 30},
+ want: "\n 30\n",
+ },
+ {
+ name: "xml format with xml.Marshaler",
+ format: "xml",
+ value: &mockXMLMarshaler{elm: "test string"},
+ want: "test string",
+ },
+ {
+ name: "xml format with error from xml.Marshaler",
+ format: "xml",
+ value: &mockXMLMarshaler{err: errors.New("marshal error!!1")},
+ wantErr: "render: marshal error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "xml format with error writing to writer",
+ format: "xml",
+ writeErr: errors.New("write error!!1"),
+ value: struct {
+ XMLName xml.Name `xml:"user"`
+ Age int `xml:"age"`
+ }{Age: 30},
+ wantErr: "render: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "xml format with invalid value",
+ format: "xml",
+ value: make(chan int),
+ wantErr: "render: xml: unsupported type: chan int",
+ wantErrIs: []error{render.Err},
+ },
+ // "yaml" format.
+ {
+ name: "yaml format",
+ format: "yaml",
+ value: map[string]int{"age": 30},
+ want: "age: 30\n",
+ },
+ {
+ name: "yaml format with nested structure",
+ format: "yaml",
+ value: map[string]any{
+ "user": map[string]any{
+ "age": 30,
+ "name": "John Doe",
+ },
+ },
+ want: "user:\n age: 30\n name: John Doe\n",
+ },
+ {
+ name: "yaml format with yaml.Marshaler",
+ format: "yaml",
+ value: &mockYAMLMarshaler{val: map[string]int{"age": 30}},
+ want: "age: 30\n",
+ },
+ {
+ name: "yaml format with error from yaml.Marshaler",
+ format: "yaml",
+ value: &mockYAMLMarshaler{err: errors.New("mock error")},
+ wantErr: "render: mock error",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "yaml format with error writing to writer",
+ format: "yaml",
+ writeErr: errors.New("write error!!1"),
+ value: map[string]int{"age": 30},
+ wantErr: "render: yaml: write error: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "yaml format with invalid type",
+ format: "yaml",
+ value: make(chan int),
+ wantPanic: "cannot marshal type: chan int",
+ },
+ // "yml" format.
+ {
+ name: "yml format",
+ format: "yml",
+ value: map[string]int{"age": 30},
+ want: "age: 30\n",
+ },
+ {
+ name: "yml format with nested structure",
+ format: "yml",
+ value: map[string]any{
+ "user": map[string]any{
+ "age": 30,
+ "name": "John Doe",
+ },
+ },
+ want: "user:\n age: 30\n name: John Doe\n",
+ },
+ {
+ name: "yml format with yaml.Marshaler",
+ format: "yml",
+ value: &mockYAMLMarshaler{val: map[string]int{"age": 30}},
+ want: "age: 30\n",
+ },
+ {
+ name: "yml format with error from yaml.Marshaler",
+ format: "yml",
+ value: &mockYAMLMarshaler{err: errors.New("mock error")},
+ wantErr: "render: mock error",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "yml format with error writing to writer",
+ format: "yml",
+ writeErr: errors.New("write error!!1"),
+ value: map[string]int{"age": 30},
+ wantErr: "render: yaml: write error: write error!!1",
+ wantErrIs: []error{render.Err},
+ },
+ {
+ name: "yml format with invalid type",
+ format: "yml",
+ value: make(chan int),
+ wantPanic: "cannot marshal type: chan int",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ w := &mockWriter{WriteErr: tt.writeErr}
+
+ var err error
+ var panicRes any
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ panicRes = r
+ }
+ }()
+ err = render.Render(w, tt.format, tt.value)
+ }()
+
+ got := w.String()
+
+ if tt.wantPanic != "" {
+ assert.Equal(t, tt.wantPanic, panicRes)
+ }
+
+ if tt.wantErr != "" {
+ assert.EqualError(t, err, tt.wantErr)
+ }
+ for _, e := range tt.wantErrIs {
+ assert.ErrorIs(t, err, e)
+ }
+
+ if tt.wantPanic == "" &&
+ tt.wantErr == "" && len(tt.wantErrIs) == 0 {
+ assert.NoError(t, err)
+ assert.Equal(t, tt.want, got)
+ }
+ })
+ }
+}
diff --git a/stringer.go b/stringer.go
index f76b21e..51ee7e0 100644
--- a/stringer.go
+++ b/stringer.go
@@ -19,7 +19,7 @@ func (s *Stringer) Render(w io.Writer, v any) error {
return ErrCannotRender
}
- _, err := fmt.Fprint(w, x.String())
+ _, err := w.Write([]byte(x.String()))
if err != nil {
return fmt.Errorf("%w: %w", Err, err)
}
diff --git a/stringer_test.go b/stringer_test.go
index 2096b39..b328e71 100644
--- a/stringer_test.go
+++ b/stringer_test.go
@@ -2,17 +2,19 @@ package render_test
import (
"errors"
+ "fmt"
"testing"
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
type mockStringer struct {
value string
}
+var _ fmt.Stringer = (*mockStringer)(nil)
+
func (ms *mockStringer) String() string {
return ms.value
}
@@ -47,16 +49,12 @@ func TestStringer_Render(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &render.Stringer{}
-
- var err error
- var got string
w := &mockWriter{WriteErr: tt.writeErr}
- err = s.Render(w, tt.value)
- got = w.String()
+ err := s.Render(w, tt.value)
+ got := w.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -64,7 +62,7 @@ func TestStringer_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/writer_to_test.go b/writer_to_test.go
index 0183ad1..709109b 100644
--- a/writer_to_test.go
+++ b/writer_to_test.go
@@ -8,7 +8,6 @@ import (
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
type mockWriterTo struct {
@@ -16,6 +15,8 @@ type mockWriterTo struct {
err error
}
+var _ io.WriterTo = (*mockWriterTo)(nil)
+
func (m *mockWriterTo) WriteTo(w io.Writer) (int64, error) {
n, err := w.Write([]byte(m.value))
@@ -59,16 +60,12 @@ func TestWriterTo_Render(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
wt := &render.WriterTo{}
-
- var err error
- var got string
w := &bytes.Buffer{}
- err = wt.Render(w, tt.value)
- got = w.String()
+ err := wt.Render(w, tt.value)
+ got := w.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -76,7 +73,7 @@ func TestWriterTo_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/xml_test.go b/xml_test.go
index 1209270..7f28913 100644
--- a/xml_test.go
+++ b/xml_test.go
@@ -3,13 +3,33 @@ package render_test
import (
"bytes"
"encoding/xml"
+ "errors"
"testing"
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
+type mockXMLMarshaler struct {
+ elm string
+ err error
+}
+
+var _ xml.Marshaler = (*mockXMLMarshaler)(nil)
+
+func (mxm *mockXMLMarshaler) MarshalXML(
+ e *xml.Encoder,
+ start xml.StartElement,
+) error {
+ err := e.EncodeElement(mxm.elm, start)
+
+ if mxm.err != nil {
+ return mxm.err
+ }
+
+ return err
+}
+
func TestXML_Render(t *testing.T) {
tests := []struct {
name string
@@ -33,15 +53,14 @@ func TestXML_Render(t *testing.T) {
{
name: "simple object with pretty",
pretty: true,
- indent: " ",
value: struct {
XMLName xml.Name `xml:"user"`
Age int `xml:"age"`
}{Age: 30},
- want: "\n 30\n",
+ want: "\n 30\n",
},
{
- name: "with prefix and indent",
+ name: "pretty with prefix and indent",
pretty: true,
prefix: "//",
indent: "\t",
@@ -51,6 +70,28 @@ func TestXML_Render(t *testing.T) {
}{Age: 30},
want: "//\n//\t30\n//",
},
+ {
+ name: "prefix and indent without pretty",
+ pretty: false,
+ prefix: "//",
+ indent: "\t",
+ value: struct {
+ XMLName xml.Name `xml:"user"`
+ Age int `xml:"age"`
+ }{Age: 30},
+ want: `30`,
+ },
+ {
+ name: "implements xml.Marshaler",
+ value: &mockXMLMarshaler{elm: "test string"},
+ want: "test string",
+ },
+ {
+ name: "error from xml.Marshaler",
+ value: &mockXMLMarshaler{err: errors.New("mock error")},
+ wantErr: "render: mock error",
+ wantErrIs: []error{render.Err},
+ },
{
name: "invalid value",
pretty: false,
@@ -67,12 +108,12 @@ func TestXML_Render(t *testing.T) {
Prefix: tt.prefix,
Indent: tt.indent,
}
-
var buf bytes.Buffer
+
err := x.Render(&buf, tt.value)
+ got := buf.String()
if tt.wantErr != "" {
- require.Error(t, err)
assert.EqualError(t, err, tt.wantErr)
}
for _, e := range tt.wantErrIs {
@@ -80,8 +121,7 @@ func TestXML_Render(t *testing.T) {
}
if tt.wantErr == "" && len(tt.wantErrIs) == 0 {
- require.NoError(t, err)
- got := buf.String()
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/yaml_test.go b/yaml_test.go
index 4065875..2b930dd 100644
--- a/yaml_test.go
+++ b/yaml_test.go
@@ -2,19 +2,33 @@ package render_test
import (
"bytes"
+ "errors"
"testing"
"github.com/jimeh/go-render"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v3"
)
+type mockYAMLMarshaler struct {
+ val any
+ err error
+}
+
+var _ yaml.Marshaler = (*mockYAMLMarshaler)(nil)
+
+func (m *mockYAMLMarshaler) MarshalYAML() (any, error) {
+ return m.val, m.err
+}
+
func TestYAML_Render(t *testing.T) {
tests := []struct {
name string
indent int
value interface{}
want string
+ wantErr string
+ wantErrIs []error
wantPanic string
}{
{
@@ -44,6 +58,17 @@ func TestYAML_Render(t *testing.T) {
},
want: "user:\n age: 30\n name: John Doe\n",
},
+ {
+ name: "implements yaml.Marshaler",
+ value: &mockYAMLMarshaler{val: map[string]int{"age": 30}},
+ want: "age: 30\n",
+ },
+ {
+ name: "error from yaml.Marshaler",
+ value: &mockYAMLMarshaler{err: errors.New("mock error")},
+ wantErr: "render: mock error",
+ wantErrIs: []error{render.Err},
+ },
{
name: "invalid value",
indent: 0,
@@ -51,7 +76,6 @@ func TestYAML_Render(t *testing.T) {
wantPanic: "cannot marshal type: chan int",
},
}
-
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := &render.YAML{
@@ -70,11 +94,21 @@ func TestYAML_Render(t *testing.T) {
err = j.Render(&buf, tt.value)
}()
+ got := buf.String()
+
if tt.wantPanic != "" {
assert.Equal(t, tt.wantPanic, panicRes)
- } else {
- require.NoError(t, err)
- got := buf.String()
+ }
+ if tt.wantErr != "" {
+ assert.EqualError(t, err, tt.wantErr)
+ }
+ for _, e := range tt.wantErrIs {
+ assert.ErrorIs(t, err, e)
+ }
+
+ if tt.wantPanic == "" &&
+ tt.wantErr == "" && len(tt.wantErrIs) == 0 {
+ assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})