forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/davecgh/go-spew/spew/format.go

722 lines
11 KiB
Go
Raw Normal View History

2024-04-26 19:30:35 +00:00
/*
2024-04-26 19:30:35 +00:00
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
2024-04-26 19:30:35 +00:00
*
2024-04-26 19:30:35 +00:00
* Permission to use, copy, modify, and distribute this software for any
2024-04-26 19:30:35 +00:00
* purpose with or without fee is hereby granted, provided that the above
2024-04-26 19:30:35 +00:00
* copyright notice and this permission notice appear in all copies.
2024-04-26 19:30:35 +00:00
*
2024-04-26 19:30:35 +00:00
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2024-04-26 19:30:35 +00:00
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2024-04-26 19:30:35 +00:00
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2024-04-26 19:30:35 +00:00
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2024-04-26 19:30:35 +00:00
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2024-04-26 19:30:35 +00:00
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2024-04-26 19:30:35 +00:00
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2024-04-26 19:30:35 +00:00
*/
package spew
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
)
// supportedFlags is a list of all the character flags supported by fmt package.
2024-04-26 19:30:35 +00:00
const supportedFlags = "0-+# "
// formatState implements the fmt.Formatter interface and contains information
2024-04-26 19:30:35 +00:00
// about the state of a formatting operation. The NewFormatter function can
2024-04-26 19:30:35 +00:00
// be used to get a new Formatter which can be used directly as arguments
2024-04-26 19:30:35 +00:00
// in standard fmt package printing calls.
2024-04-26 19:30:35 +00:00
type formatState struct {
value interface{}
fs fmt.State
depth int
pointers map[uintptr]int
2024-04-26 19:30:35 +00:00
ignoreNextType bool
cs *ConfigState
2024-04-26 19:30:35 +00:00
}
// buildDefaultFormat recreates the original format string without precision
2024-04-26 19:30:35 +00:00
// and width information to pass in to fmt.Sprintf in the case of an
2024-04-26 19:30:35 +00:00
// unrecognized type. Unless new types are added to the language, this
2024-04-26 19:30:35 +00:00
// function won't ever be called.
2024-04-26 19:30:35 +00:00
func (f *formatState) buildDefaultFormat() (format string) {
2024-04-26 19:30:35 +00:00
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
2024-04-26 19:30:35 +00:00
if f.fs.Flag(int(flag)) {
2024-04-26 19:30:35 +00:00
buf.WriteRune(flag)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
buf.WriteRune('v')
format = buf.String()
2024-04-26 19:30:35 +00:00
return format
2024-04-26 19:30:35 +00:00
}
// constructOrigFormat recreates the original format string including precision
2024-04-26 19:30:35 +00:00
// and width information to pass along to the standard fmt package. This allows
2024-04-26 19:30:35 +00:00
// automatic deferral of all format strings this package doesn't support.
2024-04-26 19:30:35 +00:00
func (f *formatState) constructOrigFormat(verb rune) (format string) {
2024-04-26 19:30:35 +00:00
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
2024-04-26 19:30:35 +00:00
if f.fs.Flag(int(flag)) {
2024-04-26 19:30:35 +00:00
buf.WriteRune(flag)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
if width, ok := f.fs.Width(); ok {
2024-04-26 19:30:35 +00:00
buf.WriteString(strconv.Itoa(width))
2024-04-26 19:30:35 +00:00
}
if precision, ok := f.fs.Precision(); ok {
2024-04-26 19:30:35 +00:00
buf.Write(precisionBytes)
2024-04-26 19:30:35 +00:00
buf.WriteString(strconv.Itoa(precision))
2024-04-26 19:30:35 +00:00
}
buf.WriteRune(verb)
format = buf.String()
2024-04-26 19:30:35 +00:00
return format
2024-04-26 19:30:35 +00:00
}
// unpackValue returns values inside of non-nil interfaces when possible and
2024-04-26 19:30:35 +00:00
// ensures that types for values which have been unpacked from an interface
2024-04-26 19:30:35 +00:00
// are displayed when the show types flag is also set.
2024-04-26 19:30:35 +00:00
// This is useful for data types like structs, arrays, slices, and maps which
2024-04-26 19:30:35 +00:00
// can contain varying types packed inside an interface.
2024-04-26 19:30:35 +00:00
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
2024-04-26 19:30:35 +00:00
if v.Kind() == reflect.Interface {
2024-04-26 19:30:35 +00:00
f.ignoreNextType = false
2024-04-26 19:30:35 +00:00
if !v.IsNil() {
2024-04-26 19:30:35 +00:00
v = v.Elem()
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
return v
2024-04-26 19:30:35 +00:00
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
2024-04-26 19:30:35 +00:00
func (f *formatState) formatPtr(v reflect.Value) {
2024-04-26 19:30:35 +00:00
// Display nil if top level pointer is nil.
2024-04-26 19:30:35 +00:00
showTypes := f.fs.Flag('#')
2024-04-26 19:30:35 +00:00
if v.IsNil() && (!showTypes || f.ignoreNextType) {
2024-04-26 19:30:35 +00:00
f.fs.Write(nilAngleBytes)
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
// Remove pointers at or below the current depth from map used to detect
2024-04-26 19:30:35 +00:00
// circular refs.
2024-04-26 19:30:35 +00:00
for k, depth := range f.pointers {
2024-04-26 19:30:35 +00:00
if depth >= f.depth {
2024-04-26 19:30:35 +00:00
delete(f.pointers, k)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
// Keep list of all dereferenced pointers to possibly show later.
2024-04-26 19:30:35 +00:00
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by derferencing
2024-04-26 19:30:35 +00:00
// pointers and unpacking interfaces down the chain while detecting circular
2024-04-26 19:30:35 +00:00
// references.
2024-04-26 19:30:35 +00:00
nilFound := false
2024-04-26 19:30:35 +00:00
cycleFound := false
2024-04-26 19:30:35 +00:00
indirects := 0
2024-04-26 19:30:35 +00:00
ve := v
2024-04-26 19:30:35 +00:00
for ve.Kind() == reflect.Ptr {
2024-04-26 19:30:35 +00:00
if ve.IsNil() {
2024-04-26 19:30:35 +00:00
nilFound = true
2024-04-26 19:30:35 +00:00
break
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
indirects++
2024-04-26 19:30:35 +00:00
addr := ve.Pointer()
2024-04-26 19:30:35 +00:00
pointerChain = append(pointerChain, addr)
2024-04-26 19:30:35 +00:00
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
2024-04-26 19:30:35 +00:00
cycleFound = true
2024-04-26 19:30:35 +00:00
indirects--
2024-04-26 19:30:35 +00:00
break
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.pointers[addr] = f.depth
ve = ve.Elem()
2024-04-26 19:30:35 +00:00
if ve.Kind() == reflect.Interface {
2024-04-26 19:30:35 +00:00
if ve.IsNil() {
2024-04-26 19:30:35 +00:00
nilFound = true
2024-04-26 19:30:35 +00:00
break
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
ve = ve.Elem()
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
// Display type or indirection level depending on flags.
2024-04-26 19:30:35 +00:00
if showTypes && !f.ignoreNextType {
2024-04-26 19:30:35 +00:00
f.fs.Write(openParenBytes)
2024-04-26 19:30:35 +00:00
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
2024-04-26 19:30:35 +00:00
f.fs.Write([]byte(ve.Type().String()))
2024-04-26 19:30:35 +00:00
f.fs.Write(closeParenBytes)
2024-04-26 19:30:35 +00:00
} else {
2024-04-26 19:30:35 +00:00
if nilFound || cycleFound {
2024-04-26 19:30:35 +00:00
indirects += strings.Count(ve.Type().String(), "*")
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.fs.Write(openAngleBytes)
2024-04-26 19:30:35 +00:00
f.fs.Write([]byte(strings.Repeat("*", indirects)))
2024-04-26 19:30:35 +00:00
f.fs.Write(closeAngleBytes)
2024-04-26 19:30:35 +00:00
}
// Display pointer information depending on flags.
2024-04-26 19:30:35 +00:00
if f.fs.Flag('+') && (len(pointerChain) > 0) {
2024-04-26 19:30:35 +00:00
f.fs.Write(openParenBytes)
2024-04-26 19:30:35 +00:00
for i, addr := range pointerChain {
2024-04-26 19:30:35 +00:00
if i > 0 {
2024-04-26 19:30:35 +00:00
f.fs.Write(pointerChainBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
printHexPtr(f.fs, addr)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.fs.Write(closeParenBytes)
2024-04-26 19:30:35 +00:00
}
// Display dereferenced value.
2024-04-26 19:30:35 +00:00
switch {
2024-04-26 19:30:35 +00:00
case nilFound:
2024-04-26 19:30:35 +00:00
f.fs.Write(nilAngleBytes)
case cycleFound:
2024-04-26 19:30:35 +00:00
f.fs.Write(circularShortBytes)
default:
2024-04-26 19:30:35 +00:00
f.ignoreNextType = true
2024-04-26 19:30:35 +00:00
f.format(ve)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
// format is the main workhorse for providing the Formatter interface. It
2024-04-26 19:30:35 +00:00
// uses the passed reflect value to figure out what kind of object we are
2024-04-26 19:30:35 +00:00
// dealing with and formats it appropriately. It is a recursive function,
2024-04-26 19:30:35 +00:00
// however circular data structures are detected and handled properly.
2024-04-26 19:30:35 +00:00
func (f *formatState) format(v reflect.Value) {
2024-04-26 19:30:35 +00:00
// Handle invalid reflect values immediately.
2024-04-26 19:30:35 +00:00
kind := v.Kind()
2024-04-26 19:30:35 +00:00
if kind == reflect.Invalid {
2024-04-26 19:30:35 +00:00
f.fs.Write(invalidAngleBytes)
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
// Handle pointers specially.
2024-04-26 19:30:35 +00:00
if kind == reflect.Ptr {
2024-04-26 19:30:35 +00:00
f.formatPtr(v)
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
// Print type information unless already handled elsewhere.
2024-04-26 19:30:35 +00:00
if !f.ignoreNextType && f.fs.Flag('#') {
2024-04-26 19:30:35 +00:00
f.fs.Write(openParenBytes)
2024-04-26 19:30:35 +00:00
f.fs.Write([]byte(v.Type().String()))
2024-04-26 19:30:35 +00:00
f.fs.Write(closeParenBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
2024-04-26 19:30:35 +00:00
// flag is enabled.
2024-04-26 19:30:35 +00:00
if !f.cs.DisableMethods {
2024-04-26 19:30:35 +00:00
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
2024-04-26 19:30:35 +00:00
if handled := handleMethods(f.cs, f.fs, v); handled {
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
switch kind {
2024-04-26 19:30:35 +00:00
case reflect.Invalid:
2024-04-26 19:30:35 +00:00
// Do nothing. We should never get here since invalid has already
2024-04-26 19:30:35 +00:00
// been handled above.
case reflect.Bool:
2024-04-26 19:30:35 +00:00
printBool(f.fs, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
2024-04-26 19:30:35 +00:00
printInt(f.fs, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
2024-04-26 19:30:35 +00:00
printUint(f.fs, v.Uint(), 10)
case reflect.Float32:
2024-04-26 19:30:35 +00:00
printFloat(f.fs, v.Float(), 32)
case reflect.Float64:
2024-04-26 19:30:35 +00:00
printFloat(f.fs, v.Float(), 64)
case reflect.Complex64:
2024-04-26 19:30:35 +00:00
printComplex(f.fs, v.Complex(), 32)
case reflect.Complex128:
2024-04-26 19:30:35 +00:00
printComplex(f.fs, v.Complex(), 64)
case reflect.Slice:
2024-04-26 19:30:35 +00:00
if v.IsNil() {
2024-04-26 19:30:35 +00:00
f.fs.Write(nilAngleBytes)
2024-04-26 19:30:35 +00:00
break
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
fallthrough
case reflect.Array:
2024-04-26 19:30:35 +00:00
f.fs.Write(openBracketBytes)
2024-04-26 19:30:35 +00:00
f.depth++
2024-04-26 19:30:35 +00:00
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
2024-04-26 19:30:35 +00:00
f.fs.Write(maxShortBytes)
2024-04-26 19:30:35 +00:00
} else {
2024-04-26 19:30:35 +00:00
numEntries := v.Len()
2024-04-26 19:30:35 +00:00
for i := 0; i < numEntries; i++ {
2024-04-26 19:30:35 +00:00
if i > 0 {
2024-04-26 19:30:35 +00:00
f.fs.Write(spaceBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.ignoreNextType = true
2024-04-26 19:30:35 +00:00
f.format(f.unpackValue(v.Index(i)))
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.depth--
2024-04-26 19:30:35 +00:00
f.fs.Write(closeBracketBytes)
case reflect.String:
2024-04-26 19:30:35 +00:00
f.fs.Write([]byte(v.String()))
case reflect.Interface:
2024-04-26 19:30:35 +00:00
// The only time we should get here is for nil interfaces due to
2024-04-26 19:30:35 +00:00
// unpackValue calls.
2024-04-26 19:30:35 +00:00
if v.IsNil() {
2024-04-26 19:30:35 +00:00
f.fs.Write(nilAngleBytes)
2024-04-26 19:30:35 +00:00
}
case reflect.Ptr:
2024-04-26 19:30:35 +00:00
// Do nothing. We should never get here since pointers have already
2024-04-26 19:30:35 +00:00
// been handled above.
case reflect.Map:
2024-04-26 19:30:35 +00:00
// nil maps should be indicated as different than empty maps
2024-04-26 19:30:35 +00:00
if v.IsNil() {
2024-04-26 19:30:35 +00:00
f.fs.Write(nilAngleBytes)
2024-04-26 19:30:35 +00:00
break
2024-04-26 19:30:35 +00:00
}
f.fs.Write(openMapBytes)
2024-04-26 19:30:35 +00:00
f.depth++
2024-04-26 19:30:35 +00:00
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
2024-04-26 19:30:35 +00:00
f.fs.Write(maxShortBytes)
2024-04-26 19:30:35 +00:00
} else {
2024-04-26 19:30:35 +00:00
keys := v.MapKeys()
2024-04-26 19:30:35 +00:00
if f.cs.SortKeys {
2024-04-26 19:30:35 +00:00
sortValues(keys, f.cs)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
for i, key := range keys {
2024-04-26 19:30:35 +00:00
if i > 0 {
2024-04-26 19:30:35 +00:00
f.fs.Write(spaceBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.ignoreNextType = true
2024-04-26 19:30:35 +00:00
f.format(f.unpackValue(key))
2024-04-26 19:30:35 +00:00
f.fs.Write(colonBytes)
2024-04-26 19:30:35 +00:00
f.ignoreNextType = true
2024-04-26 19:30:35 +00:00
f.format(f.unpackValue(v.MapIndex(key)))
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.depth--
2024-04-26 19:30:35 +00:00
f.fs.Write(closeMapBytes)
case reflect.Struct:
2024-04-26 19:30:35 +00:00
numFields := v.NumField()
2024-04-26 19:30:35 +00:00
f.fs.Write(openBraceBytes)
2024-04-26 19:30:35 +00:00
f.depth++
2024-04-26 19:30:35 +00:00
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
2024-04-26 19:30:35 +00:00
f.fs.Write(maxShortBytes)
2024-04-26 19:30:35 +00:00
} else {
2024-04-26 19:30:35 +00:00
vt := v.Type()
2024-04-26 19:30:35 +00:00
for i := 0; i < numFields; i++ {
2024-04-26 19:30:35 +00:00
if i > 0 {
2024-04-26 19:30:35 +00:00
f.fs.Write(spaceBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
vtf := vt.Field(i)
2024-04-26 19:30:35 +00:00
if f.fs.Flag('+') || f.fs.Flag('#') {
2024-04-26 19:30:35 +00:00
f.fs.Write([]byte(vtf.Name))
2024-04-26 19:30:35 +00:00
f.fs.Write(colonBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.format(f.unpackValue(v.Field(i)))
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
f.depth--
2024-04-26 19:30:35 +00:00
f.fs.Write(closeBraceBytes)
case reflect.Uintptr:
2024-04-26 19:30:35 +00:00
printHexPtr(f.fs, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
2024-04-26 19:30:35 +00:00
printHexPtr(f.fs, v.Pointer())
// There were not any other types at the time this code was written, but
2024-04-26 19:30:35 +00:00
// fall back to letting the default fmt package handle it if any get added.
2024-04-26 19:30:35 +00:00
default:
2024-04-26 19:30:35 +00:00
format := f.buildDefaultFormat()
2024-04-26 19:30:35 +00:00
if v.CanInterface() {
2024-04-26 19:30:35 +00:00
fmt.Fprintf(f.fs, format, v.Interface())
2024-04-26 19:30:35 +00:00
} else {
2024-04-26 19:30:35 +00:00
fmt.Fprintf(f.fs, format, v.String())
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
}
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
2024-04-26 19:30:35 +00:00
// details.
2024-04-26 19:30:35 +00:00
func (f *formatState) Format(fs fmt.State, verb rune) {
2024-04-26 19:30:35 +00:00
f.fs = fs
// Use standard formatting for verbs that are not v.
2024-04-26 19:30:35 +00:00
if verb != 'v' {
2024-04-26 19:30:35 +00:00
format := f.constructOrigFormat(verb)
2024-04-26 19:30:35 +00:00
fmt.Fprintf(fs, format, f.value)
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
if f.value == nil {
2024-04-26 19:30:35 +00:00
if fs.Flag('#') {
2024-04-26 19:30:35 +00:00
fs.Write(interfaceBytes)
2024-04-26 19:30:35 +00:00
}
2024-04-26 19:30:35 +00:00
fs.Write(nilAngleBytes)
2024-04-26 19:30:35 +00:00
return
2024-04-26 19:30:35 +00:00
}
f.format(reflect.ValueOf(f.value))
2024-04-26 19:30:35 +00:00
}
// newFormatter is a helper function to consolidate the logic from the various
2024-04-26 19:30:35 +00:00
// public methods which take varying config states.
2024-04-26 19:30:35 +00:00
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
2024-04-26 19:30:35 +00:00
fs := &formatState{value: v, cs: cs}
2024-04-26 19:30:35 +00:00
fs.pointers = make(map[uintptr]int)
2024-04-26 19:30:35 +00:00
return fs
2024-04-26 19:30:35 +00:00
}
/*
2024-04-26 19:30:35 +00:00
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
2024-04-26 19:30:35 +00:00
interface. As a result, it integrates cleanly with standard fmt package
2024-04-26 19:30:35 +00:00
printing functions. The formatter is useful for inline printing of smaller data
2024-04-26 19:30:35 +00:00
types similar to the standard %v format specifier.
2024-04-26 19:30:35 +00:00
The custom formatter only responds to the %v (most compact), %+v (adds pointer
2024-04-26 19:30:35 +00:00
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
2024-04-26 19:30:35 +00:00
combinations. Any other verbs such as %x and %q will be sent to the the
2024-04-26 19:30:35 +00:00
standard fmt package for formatting. In addition, the custom formatter ignores
2024-04-26 19:30:35 +00:00
the width and precision arguments (however they will still work on the format
2024-04-26 19:30:35 +00:00
specifiers not handled by the custom formatter).
2024-04-26 19:30:35 +00:00
Typically this function shouldn't be called directly. It is much easier to make
2024-04-26 19:30:35 +00:00
use of the custom formatter by calling one of the convenience functions such as
2024-04-26 19:30:35 +00:00
Printf, Println, or Fprintf.
2024-04-26 19:30:35 +00:00
*/
2024-04-26 19:30:35 +00:00
func NewFormatter(v interface{}) fmt.Formatter {
2024-04-26 19:30:35 +00:00
return newFormatter(&Config, v)
2024-04-26 19:30:35 +00:00
}