瀏覽代碼

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.
Dave Collins 13 年之前
父節點
當前提交
034a2a5a5e
共有 6 個文件被更改,包括 303 次插入12 次删除
  1. 2 0
      spew/config.go
  2. 13 4
      spew/doc.go
  3. 11 5
      spew/dump.go
  4. 92 0
      spew/example_test.go
  5. 9 3
      spew/format.go
  6. 176 0
      spew/spew.go

+ 2 - 0
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: " "}

+ 13 - 4
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.

+ 11 - 5
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...)
 }

+ 92 - 0
spew/example_test.go

@@ -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
+}

+ 9 - 3
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)
 }

+ 176 - 0
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
+}