diff --git a/spew/config.go b/spew/config.go index 542f457..e32b224 100644 --- a/spew/config.go +++ b/spew/config.go @@ -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: " "} \ No newline at end of file diff --git a/spew/doc.go b/spew/doc.go index 61c0091..94db62d 100644 --- a/spew/doc.go +++ b/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. diff --git a/spew/dump.go b/spew/dump.go index 067f98e..361e90e 100644 --- a/spew/dump.go +++ b/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...) } diff --git a/spew/example_test.go b/spew/example_test.go index e96d2c1..2a35010 100644 --- a/spew/example_test.go +++ b/spew/example_test.go @@ -130,3 +130,95 @@ func ExamplePrintf() { // ppui8: <**>5 // circular: {1 <*>{1 <*>}} } + +// 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) + // }, + // ExportedField: (map[interface {}]interface {}) { + // (string) "one": (bool) true + // } + // } + // (spew_test.Foo) { + // unexportedField: (spew_test.Bar) { + // flag: (spew_test.Flag) flagTwo, + // data: (uintptr) + // }, + // 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 +} diff --git a/spew/format.go b/spew/format.go index 604df9a..4a285e2 100644 --- a/spew/format.go +++ b/spew/format.go @@ -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) } diff --git a/spew/spew.go b/spew/spew.go index a599978..46fd904 100644 --- a/spew/spew.go +++ b/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 +}