refactor!: sizeable changes across the board

This commit is contained in:
2024-03-19 03:54:31 +00:00
parent a1b1ef82ad
commit 7632b1119c
22 changed files with 620 additions and 733 deletions

135
render.go
View File

@@ -1,3 +1,14 @@
// Package render provides a simple and flexible way to render a value to a
// io.Writer using different formats based on a format string argument.
//
// It allows rendering a custom type which can be marshaled to JSON, YAML, XML,
// while also supporting plain text by implementing fmt.Stringer or io.WriterTo.
// Binary output is also supported by implementing the encoding.BinaryMarshaler
// interface.
//
// Originally intended to easily implement CLI tools which can output their data
// as plain text, as well as JSON/YAML with a simple switch of a format string.
// But it can just as easily render to any io.Writer.
package render
import (
@@ -6,24 +17,43 @@ import (
)
var (
Err = fmt.Errorf("render")
// Err is the base error for the package. All errors returned by this
// package are wrapped with this error.
Err = fmt.Errorf("render")
ErrFailed = fmt.Errorf("%w: failed", Err)
// ErrCannotRender is returned when a value cannot be rendered. This may be
// due to the value not supporting the format, or the value itself not being
// renderable.
// renderable. Only Renderer implementations should return this error.
ErrCannotRender = fmt.Errorf("%w: cannot render", Err)
)
// Renderer is the interface that that individual renderers must implement.
type Renderer interface {
// FormatRenderer interface is for single format renderers, which can only
// render a single format.
type FormatRenderer interface {
// Render writes v into w in the format that the FormatRenderer supports.
//
// If v does not implement a required interface, or otherwise cannot be
// rendered to the format in question, then a ErrCannotRender error must be
// returned. Any other errors should be returned as is.
Render(w io.Writer, v any) error
}
var (
// DefaultBinary is the default binary marshaler renderer. It
// renders values using the encoding.BinaryMarshaler interface.
DefaultBinary = &Binary{}
// DefaultJSON is the default JSON renderer. It renders values using the
// encoding/json package, with pretty printing enabled.
DefaultJSON = &JSON{Pretty: true}
// DefaultText is the default text renderer, used by the package level
// Render function. It renders values using the DefaultStringer and
// DefaultWriterTo renderers. This means a value must implement either the
// fmt.Stringer or io.WriterTo interfaces to be rendered.
DefaultText = &Text{}
// DefaultXML is the default XML renderer. It renders values using the
// encoding/xml package, with pretty printing enabled.
DefaultXML = &XML{Pretty: true}
@@ -32,39 +62,10 @@ var (
// gopkg.in/yaml.v3 package, with an indentation of 2 spaces.
DefaultYAML = &YAML{Indent: 2}
// DefaultWriterTo is the default writer to renderer. It renders values
// using the io.WriterTo interface.
DefaultWriterTo = &WriterTo{}
// DefaultStringer is the default stringer renderer. It renders values
// using the fmt.Stringer interface.
DefaultStringer = &Stringer{}
// DefaultText is the default text renderer, used by the package level
// Render function. It renders values using the DefaultStringer and
// DefaultWriterTo renderers. This means a value must implement either the
// fmt.Stringer or io.WriterTo interfaces to be rendered.
DefaultText = &MultiRenderer{
Renderers: []Renderer{DefaultStringer, DefaultWriterTo},
}
// DefaultBinary is the default binary marshaler renderer. It
// renders values using the encoding.BinaryMarshaler interface.
DefaultBinary = &Binary{}
// DefaultRenderer is the default renderer, used by the package level Render
// function.
DefaultRenderer = &FormatRenderer{map[string]Renderer{
"bin": DefaultBinary,
"binary": DefaultBinary,
"json": DefaultJSON,
"plain": DefaultText,
"text": DefaultText,
"txt": DefaultText,
"xml": DefaultXML,
"yaml": DefaultYAML,
"yml": DefaultYAML,
}}
// DefaultRenderer is used by the package level Render function. It supports
// the text", "json", and "yaml" formats. If you need to support another set
// of formats, use the New function to create a custom FormatRenderer.
DefaultRenderer = MustNew("json", "text", "yaml")
)
// Render renders the given value to the given writer using the given format.
@@ -72,23 +73,63 @@ var (
//
// By default it supports the following formats:
//
// - "text": Renders values using the fmt.Stringer and io.WriterTo interfaces.
// - "json": Renders values using the encoding/json package, with pretty
// printing enabled.
// - "yaml": Renders values using the gopkg.in/yaml.v3 package, with an
// indentation of 2 spaces.
// - "yml": Alias for "yaml".
// - "xml": Renders values using the encoding/xml package, with pretty
// printing enabled.
// - "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 ErrUnsupportedFormat error will be
// returned.
//
// If you need to support a custom set of formats, use the New function to
// create a new FormatRenderer with the formats you need. If you need new custom
// renderers, manually create a new FormatRenderer.
func Render(w io.Writer, format string, v any) error {
return DefaultRenderer.Render(w, format, v)
}
// New creates a new *FormatRenderer with support for the given formats.
//
// Supported formats are:
//
// - "binary": Renders values using DefaultBinary.
// - "json": Renders values using DefaultJSON.
// - "text": Renders values using DefaultText.
// - "xml": Renders values using DefaultXML.
// - "yaml": Renders values using DefaultYAML.
//
// If an unsupported format is given, an ErrUnsupportedFormat error will be
// returned.
func New(formats ...string) (*Renderer, error) {
renderers := map[string]FormatRenderer{}
for _, format := range formats {
switch format {
case "binary":
renderers[format] = DefaultBinary
case "json":
renderers[format] = DefaultJSON
case "text":
renderers[format] = DefaultText
case "xml":
renderers[format] = DefaultXML
case "yaml":
renderers[format] = DefaultYAML
default:
return nil, fmt.Errorf("%w: %s", ErrUnsupportedFormat, format)
}
}
return NewFormatRenderer(renderers), nil
}
// MustNew is like New, but panics if an error occurs.
func MustNew(formats ...string) *Renderer {
r, err := New(formats...)
if err != nil {
panic(err)
}
return r
}