Implement support for unqiue config instances.
This commit adds a new type, SpewState, which can be used to create instances with unique configuration options. The methods of SpewState are equivalent to the top-level functions. Full documentation and examples are included.
This commit is contained in:
parent
40eb25a95d
commit
034a2a5a5e
|
@ -55,3 +55,5 @@ type ConfigState struct {
|
|||
// Config is the active configuration in use by spew. The configuration
|
||||
// can be changed by modifying the contents of spew.Config.
|
||||
var Config ConfigState = ConfigState{Indent: " "}
|
||||
|
||||
var defaultConfig = ConfigState{Indent: " "}
|
17
spew/doc.go
17
spew/doc.go
|
@ -58,20 +58,29 @@ printing style, use the convenience wrappers Printf, Fprintf, etc with either
|
|||
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
via the spew.Config global.
|
||||
|
||||
It is also possible to create a SpewState instance which provides a unique
|
||||
ConfigState accessible via the Config method. The methods of SpewState are
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the SpewState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
spew.Config.MaxDepth
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
spew.Config.Indent
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
spew.Config.DisableMethods
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
spew.Config.DisablePointerMethods
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
|
16
spew/dump.go
16
spew/dump.go
|
@ -281,9 +281,9 @@ func (d *dumpState) dump(v reflect.Value) {
|
|||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
// fdump is a helper function to consolidate the logic from the various public
|
||||
// methods which take varying writers and config states.
|
||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||
for _, arg := range a {
|
||||
if arg == nil {
|
||||
w.Write(interfaceBytes)
|
||||
|
@ -292,13 +292,19 @@ func Fdump(w io.Writer, a ...interface{}) {
|
|||
continue
|
||||
}
|
||||
|
||||
d := dumpState{w: w, cs: &Config}
|
||||
d := dumpState{w: w, cs: cs}
|
||||
d.pointers = make(map[uintptr]int)
|
||||
d.dump(reflect.ValueOf(arg))
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(&Config, w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
|
@ -320,5 +326,5 @@ spew.Config. See ConfigState for options documentation.
|
|||
See Fdump if you would prefer dump to an arbitrary io.Writer.
|
||||
*/
|
||||
func Dump(a ...interface{}) {
|
||||
Fdump(os.Stdout, a...)
|
||||
fdump(&Config, os.Stdout, a...)
|
||||
}
|
||||
|
|
|
@ -130,3 +130,95 @@ func ExamplePrintf() {
|
|||
// ppui8: <**>5
|
||||
// circular: {1 <*>{1 <*><shown>}}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use a SpewState.
|
||||
func ExampleSpewState() {
|
||||
// A SpewState does not need initialization.
|
||||
ss := new(spew.SpewState) // or var ss spew.SpewState
|
||||
|
||||
// Modify the indent level of the SpewState only. The global configuration
|
||||
// is not modified.
|
||||
ssc := ss.Config()
|
||||
ssc.Indent = "\t"
|
||||
|
||||
// Output using the SpewState instance.
|
||||
v := map[string]int{"one": 1}
|
||||
ss.Printf("v: %v\n", v)
|
||||
ss.Dump(v)
|
||||
|
||||
// Output:
|
||||
// v: map[one:1]
|
||||
// (map[string]int) {
|
||||
// (string) "one": (int) 1
|
||||
// }
|
||||
}
|
||||
|
||||
// This example demonstrates how to use a SpewState.Dump to dump variables to
|
||||
// stdout
|
||||
func ExampleSpewState_Dump() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// A SpewState does not need initialization.
|
||||
ss := new(spew.SpewState) // or var ss spew.SpewState
|
||||
ss2 := new(spew.SpewState) // or var ss2 spew.SpewState
|
||||
|
||||
// Modify the indent level of the first SpewState only.
|
||||
ssc := ss.Config()
|
||||
ssc.Indent = "\t"
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{Flag(flagTwo), uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
|
||||
// Dump using the SpewState instances.
|
||||
ss.Dump(s1)
|
||||
ss2.Dump(s1)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// flag: (spew_test.Flag) flagTwo,
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) {
|
||||
// (string) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// flag: (spew_test.Flag) flagTwo,
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) {
|
||||
// (string) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use SpewState.Printf to display a variable
|
||||
// with a format string and inline formatting.
|
||||
func ExampleSpewState_Printf() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// A SpewState does not need initialization.
|
||||
ss := new(spew.SpewState) // or var ss spew.SpewState
|
||||
ss2 := new(spew.SpewState) // or var ss2 spew.SpewState
|
||||
|
||||
// Modify the method handling of the first SpewState only.
|
||||
ssc := ss.Config()
|
||||
ssc.DisableMethods = true
|
||||
|
||||
// This is of type Flag which implements a Stringer and has raw value 1.
|
||||
f := flagTwo
|
||||
|
||||
// Dump using the SpewState instances.
|
||||
ss.Printf("f: %v\n", f)
|
||||
ss2.Printf("f: %v\n", f)
|
||||
|
||||
// Output:
|
||||
// f: 1
|
||||
// f: flagTwo
|
||||
}
|
||||
|
|
|
@ -316,6 +316,14 @@ func (f *formatState) Format(fs fmt.State, verb rune) {
|
|||
f.buffer.WriteTo(fs)
|
||||
}
|
||||
|
||||
// newFormatter is a helper function to consolidate the logic from the various
|
||||
// public methods which take varying config states.
|
||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: cs}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
|
@ -333,7 +341,5 @@ use of the custom formatter by calling one of the convenience functions such as
|
|||
Printf, Println, or Printf.
|
||||
*/
|
||||
func NewFormatter(v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: &Config}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
return newFormatter(&Config, v)
|
||||
}
|
||||
|
|
176
spew/spew.go
176
spew/spew.go
|
@ -19,6 +19,7 @@ package spew
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
|
@ -113,3 +114,178 @@ func convertArgs(args []interface{}) (formatters []interface{}) {
|
|||
}
|
||||
return formatters
|
||||
}
|
||||
|
||||
// SpewState provides a context which can have its own configuration options.
|
||||
// The configuration options can be manipulated via the Config method. The
|
||||
// methods of SpewState are equivalent to the top-level functions.
|
||||
//
|
||||
// A SpewState does not need any special initialization, so new(SpewState) or
|
||||
// just declaring a SpewState variable, is sufficient to initialilize a
|
||||
// SpewState using the default configuration options.
|
||||
type SpewState struct {
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// Config returns a pointer to the active ConfigState for the SpewState
|
||||
// instance. Set the fields of the returned structure to the desired
|
||||
// configuration settings for the instance.
|
||||
func (s *SpewState) Config() (cs *ConfigState) {
|
||||
if s.cs == nil {
|
||||
cs := defaultConfig
|
||||
s.cs = &cs
|
||||
}
|
||||
return s.cs
|
||||
}
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the formatted string as a value that satisfies error. See NewFormatter
|
||||
// for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a Formatter interface returned by s.NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by s.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(s.NewFormatter(a), s.NewFormatter(b))
|
||||
func (s *SpewState) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(s.convertArgs(a)...)
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v and %+v verb combinations. Any
|
||||
other variations such as %x, %q, and %#v will be sent to the the standard fmt
|
||||
package for formatting. In addition, the custom formatter ignores the width and
|
||||
precision arguments (however they will still work on the format specifiers not
|
||||
handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
s.Printf, s.Println, or s.Printf.
|
||||
*/
|
||||
func (s *SpewState) NewFormatter(v interface{}) fmt.Formatter {
|
||||
// The Config method creates the config state if needed, so call it instead
|
||||
// of using s.cs directly to ensure the zero value SpewState is sane.
|
||||
return newFormatter(s.Config(), v)
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func (s *SpewState) Fdump(w io.Writer, a ...interface{}) {
|
||||
// The Config method creates the config state if needed, so call it instead
|
||||
// of using s.cs directly to ensure the zero value SpewState is sane.
|
||||
fdump(s.Config(), w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom error/Stringer interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the error/Stringer interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
|
||||
The configuration options are controlled by accessing the ConfigState associated
|
||||
with s via the Config method. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dump to an arbitrary io.Writer.
|
||||
*/
|
||||
func (s *SpewState) Dump(a ...interface{}) {
|
||||
// The Config method creates the config state if needed, so call it instead
|
||||
// of using s.cs directly to ensure the zero value SpewState is sane.
|
||||
fdump(s.Config(), os.Stdout, a...)
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a spew Formatter interface using
|
||||
// the ConfigState associated with s.
|
||||
func (s *SpewState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
// The Config method creates the config state if needed, so call it instead
|
||||
// of using s.cs directly to ensure the zero value SpewState is sane.
|
||||
cs := s.Config()
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = newFormatter(cs, arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue