mirror of
https://github.com/jimeh/go-render.git
synced 2026-02-19 11:26:39 +00:00
This is yet another drastic refactor of public API and concepts. Hopefully the last one, as I'm now fairly happy with things.
121 lines
3.3 KiB
Go
121 lines
3.3 KiB
Go
package render
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// ErrUnsupportedFormat is returned when a format is not supported by any
|
|
// Handler.
|
|
var ErrUnsupportedFormat = fmt.Errorf("%w: unsupported format", Err)
|
|
|
|
// Renderer exposes methods for rendering values to different formats. The
|
|
// Renderer delegates rendering to format specific handlers based on the format
|
|
// string given.
|
|
type Renderer struct {
|
|
// Handlers is a map of format names to Handler. When Render is called,
|
|
// the format is used to look up the Handler to use.
|
|
Handlers map[string]Handler
|
|
}
|
|
|
|
// New returns a new Renderer that delegates rendering to the specified
|
|
// Handlers.
|
|
//
|
|
// Any Handlers which implement the FormatsHandler interface, will also be set
|
|
// as the handler for all format strings returned by Formats() on the handler.
|
|
func New(handlers map[string]Handler) *Renderer {
|
|
r := &Renderer{Handlers: make(map[string]Handler, len(handlers))}
|
|
|
|
for format, handler := range handlers {
|
|
r.Add(format, handler)
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// Add adds a Handler to the Renderer. If the handler implements the
|
|
// FormatsHandler interface, the handler will be added for all formats returned
|
|
// by Formats().
|
|
func (r *Renderer) Add(format string, handler Handler) {
|
|
if format != "" {
|
|
r.Handlers[strings.ToLower(format)] = handler
|
|
}
|
|
|
|
if x, ok := handler.(FormatsHandler); ok {
|
|
for _, f := range x.Formats() {
|
|
if f != "" && f != format {
|
|
r.Handlers[strings.ToLower(f)] = handler
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render renders a value to the given io.Writer using the specified format.
|
|
//
|
|
// If pretty is true, it will attempt to render the value with pretty
|
|
// formatting if the underlying Handler supports pretty formatting.
|
|
//
|
|
// If the format is not supported or the value cannot be rendered to the format,
|
|
// a ErrUnsupportedFormat error is returned.
|
|
func (r *Renderer) Render(
|
|
w io.Writer,
|
|
format string,
|
|
pretty bool,
|
|
v any,
|
|
) error {
|
|
handler, ok := r.Handlers[strings.ToLower(format)]
|
|
if !ok {
|
|
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, format)
|
|
}
|
|
|
|
prettyHandler, ok := handler.(PrettyHandler)
|
|
var err error
|
|
if pretty && ok {
|
|
err = prettyHandler.RenderPretty(w, v)
|
|
} else {
|
|
err = handler.Render(w, v)
|
|
}
|
|
|
|
if err != nil {
|
|
if errors.Is(err, ErrCannotRender) {
|
|
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, format)
|
|
}
|
|
|
|
// Ensure that the error is wrapped with ErrFailed if it is not already.
|
|
if !errors.Is(err, ErrFailed) {
|
|
return fmt.Errorf("%w: %w", ErrFailed, err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Compact is a convenience method that calls Render with pretty set to false.
|
|
func (r *Renderer) Compact(w io.Writer, format string, v any) error {
|
|
return r.Render(w, format, false, v)
|
|
}
|
|
|
|
// Pretty is a convenience method that calls Render with pretty set to true.
|
|
func (r *Renderer) Pretty(w io.Writer, format string, v any) error {
|
|
return r.Render(w, format, true, v)
|
|
}
|
|
|
|
// NewWith creates a new Renderer with the formats given, if they have handlers
|
|
// in the currener Renderer. It essentially allows to restrict a Renderer to a
|
|
// only a sub-set of supported formats.
|
|
func (r *Renderer) NewWith(formats ...string) *Renderer {
|
|
handlers := make(map[string]Handler, len(formats))
|
|
|
|
for _, format := range formats {
|
|
if r, ok := r.Handlers[strings.ToLower(format)]; ok {
|
|
handlers[format] = r
|
|
}
|
|
}
|
|
|
|
return New(handlers)
|
|
}
|