Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -51,6 +51,7 @@ clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 |
| 51 | 51 |
clone git github.com/Microsoft/hcsshim v0.5.2 |
| 52 | 52 |
clone git github.com/Microsoft/go-winio v0.3.5 |
| 53 | 53 |
clone git github.com/Sirupsen/logrus f76d643702a30fbffecdfe50831e11881c96ceb3 https://github.com/aaronlehmann/logrus |
| 54 |
+clone git github.com/davecgh/go-spew 6d212800a42e8ab5c146b8ace3490ee17e5225f9 |
|
| 54 | 55 |
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| 55 | 56 |
clone git github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git |
| 56 | 57 |
clone git github.com/gorilla/context v1.1 |
| 57 | 58 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+ISC License |
|
| 1 |
+ |
|
| 2 |
+Copyright (c) 2012-2013 Dave Collins <dave@davec.name> |
|
| 3 |
+ |
|
| 4 |
+Permission to use, copy, modify, and distribute this software for any |
|
| 5 |
+purpose with or without fee is hereby granted, provided that the above |
|
| 6 |
+copyright notice and this permission notice appear in all copies. |
|
| 7 |
+ |
|
| 8 |
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 9 |
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 10 |
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 11 |
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 12 |
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 13 |
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 14 |
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,152 @@ |
| 0 |
+// Copyright (c) 2015 Dave Collins <dave@davec.name> |
|
| 1 |
+// |
|
| 2 |
+// Permission to use, copy, modify, and distribute this software for any |
|
| 3 |
+// purpose with or without fee is hereby granted, provided that the above |
|
| 4 |
+// copyright notice and this permission notice appear in all copies. |
|
| 5 |
+// |
|
| 6 |
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 7 |
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 8 |
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 9 |
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 10 |
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 11 |
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 12 |
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 13 |
+ |
|
| 14 |
+// NOTE: Due to the following build constraints, this file will only be compiled |
|
| 15 |
+// when the code is not running on Google App Engine, compiled by GopherJS, and |
|
| 16 |
+// "-tags safe" is not added to the go build command line. The "disableunsafe" |
|
| 17 |
+// tag is deprecated and thus should not be used. |
|
| 18 |
+// +build !js,!appengine,!safe,!disableunsafe |
|
| 19 |
+ |
|
| 20 |
+package spew |
|
| 21 |
+ |
|
| 22 |
+import ( |
|
| 23 |
+ "reflect" |
|
| 24 |
+ "unsafe" |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+const ( |
|
| 28 |
+ // UnsafeDisabled is a build-time constant which specifies whether or |
|
| 29 |
+ // not access to the unsafe package is available. |
|
| 30 |
+ UnsafeDisabled = false |
|
| 31 |
+ |
|
| 32 |
+ // ptrSize is the size of a pointer on the current arch. |
|
| 33 |
+ ptrSize = unsafe.Sizeof((*byte)(nil)) |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+var ( |
|
| 37 |
+ // offsetPtr, offsetScalar, and offsetFlag are the offsets for the |
|
| 38 |
+ // internal reflect.Value fields. These values are valid before golang |
|
| 39 |
+ // commit ecccf07e7f9d which changed the format. The are also valid |
|
| 40 |
+ // after commit 82f48826c6c7 which changed the format again to mirror |
|
| 41 |
+ // the original format. Code in the init function updates these offsets |
|
| 42 |
+ // as necessary. |
|
| 43 |
+ offsetPtr = uintptr(ptrSize) |
|
| 44 |
+ offsetScalar = uintptr(0) |
|
| 45 |
+ offsetFlag = uintptr(ptrSize * 2) |
|
| 46 |
+ |
|
| 47 |
+ // flagKindWidth and flagKindShift indicate various bits that the |
|
| 48 |
+ // reflect package uses internally to track kind information. |
|
| 49 |
+ // |
|
| 50 |
+ // flagRO indicates whether or not the value field of a reflect.Value is |
|
| 51 |
+ // read-only. |
|
| 52 |
+ // |
|
| 53 |
+ // flagIndir indicates whether the value field of a reflect.Value is |
|
| 54 |
+ // the actual data or a pointer to the data. |
|
| 55 |
+ // |
|
| 56 |
+ // These values are valid before golang commit 90a7c3c86944 which |
|
| 57 |
+ // changed their positions. Code in the init function updates these |
|
| 58 |
+ // flags as necessary. |
|
| 59 |
+ flagKindWidth = uintptr(5) |
|
| 60 |
+ flagKindShift = uintptr(flagKindWidth - 1) |
|
| 61 |
+ flagRO = uintptr(1 << 0) |
|
| 62 |
+ flagIndir = uintptr(1 << 1) |
|
| 63 |
+) |
|
| 64 |
+ |
|
| 65 |
+func init() {
|
|
| 66 |
+ // Older versions of reflect.Value stored small integers directly in the |
|
| 67 |
+ // ptr field (which is named val in the older versions). Versions |
|
| 68 |
+ // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named |
|
| 69 |
+ // scalar for this purpose which unfortunately came before the flag |
|
| 70 |
+ // field, so the offset of the flag field is different for those |
|
| 71 |
+ // versions. |
|
| 72 |
+ // |
|
| 73 |
+ // This code constructs a new reflect.Value from a known small integer |
|
| 74 |
+ // and checks if the size of the reflect.Value struct indicates it has |
|
| 75 |
+ // the scalar field. When it does, the offsets are updated accordingly. |
|
| 76 |
+ vv := reflect.ValueOf(0xf00) |
|
| 77 |
+ if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
|
| 78 |
+ offsetScalar = ptrSize * 2 |
|
| 79 |
+ offsetFlag = ptrSize * 3 |
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 82 |
+ // Commit 90a7c3c86944 changed the flag positions such that the low |
|
| 83 |
+ // order bits are the kind. This code extracts the kind from the flags |
|
| 84 |
+ // field and ensures it's the correct type. When it's not, the flag |
|
| 85 |
+ // order has been changed to the newer format, so the flags are updated |
|
| 86 |
+ // accordingly. |
|
| 87 |
+ upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) |
|
| 88 |
+ upfv := *(*uintptr)(upf) |
|
| 89 |
+ flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) |
|
| 90 |
+ if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
|
| 91 |
+ flagKindShift = 0 |
|
| 92 |
+ flagRO = 1 << 5 |
|
| 93 |
+ flagIndir = 1 << 6 |
|
| 94 |
+ |
|
| 95 |
+ // Commit adf9b30e5594 modified the flags to separate the |
|
| 96 |
+ // flagRO flag into two bits which specifies whether or not the |
|
| 97 |
+ // field is embedded. This causes flagIndir to move over a bit |
|
| 98 |
+ // and means that flagRO is the combination of either of the |
|
| 99 |
+ // original flagRO bit and the new bit. |
|
| 100 |
+ // |
|
| 101 |
+ // This code detects the change by extracting what used to be |
|
| 102 |
+ // the indirect bit to ensure it's set. When it's not, the flag |
|
| 103 |
+ // order has been changed to the newer format, so the flags are |
|
| 104 |
+ // updated accordingly. |
|
| 105 |
+ if upfv&flagIndir == 0 {
|
|
| 106 |
+ flagRO = 3 << 5 |
|
| 107 |
+ flagIndir = 1 << 7 |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// unsafeReflectValue converts the passed reflect.Value into a one that bypasses |
|
| 113 |
+// the typical safety restrictions preventing access to unaddressable and |
|
| 114 |
+// unexported data. It works by digging the raw pointer to the underlying |
|
| 115 |
+// value out of the protected value and generating a new unprotected (unsafe) |
|
| 116 |
+// reflect.Value to it. |
|
| 117 |
+// |
|
| 118 |
+// This allows us to check for implementations of the Stringer and error |
|
| 119 |
+// interfaces to be used for pretty printing ordinarily unaddressable and |
|
| 120 |
+// inaccessible values such as unexported struct fields. |
|
| 121 |
+func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
|
| 122 |
+ indirects := 1 |
|
| 123 |
+ vt := v.Type() |
|
| 124 |
+ upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) |
|
| 125 |
+ rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) |
|
| 126 |
+ if rvf&flagIndir != 0 {
|
|
| 127 |
+ vt = reflect.PtrTo(v.Type()) |
|
| 128 |
+ indirects++ |
|
| 129 |
+ } else if offsetScalar != 0 {
|
|
| 130 |
+ // The value is in the scalar field when it's not one of the |
|
| 131 |
+ // reference types. |
|
| 132 |
+ switch vt.Kind() {
|
|
| 133 |
+ case reflect.Uintptr: |
|
| 134 |
+ case reflect.Chan: |
|
| 135 |
+ case reflect.Func: |
|
| 136 |
+ case reflect.Map: |
|
| 137 |
+ case reflect.Ptr: |
|
| 138 |
+ case reflect.UnsafePointer: |
|
| 139 |
+ default: |
|
| 140 |
+ upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + |
|
| 141 |
+ offsetScalar) |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ pv := reflect.NewAt(vt, upv) |
|
| 146 |
+ rv = pv |
|
| 147 |
+ for i := 0; i < indirects; i++ {
|
|
| 148 |
+ rv = rv.Elem() |
|
| 149 |
+ } |
|
| 150 |
+ return rv |
|
| 151 |
+} |
| 0 | 152 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 0 |
+// Copyright (c) 2015 Dave Collins <dave@davec.name> |
|
| 1 |
+// |
|
| 2 |
+// Permission to use, copy, modify, and distribute this software for any |
|
| 3 |
+// purpose with or without fee is hereby granted, provided that the above |
|
| 4 |
+// copyright notice and this permission notice appear in all copies. |
|
| 5 |
+// |
|
| 6 |
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 7 |
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 8 |
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 9 |
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 10 |
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 11 |
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 12 |
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 13 |
+ |
|
| 14 |
+// NOTE: Due to the following build constraints, this file will only be compiled |
|
| 15 |
+// when the code is running on Google App Engine, compiled by GopherJS, or |
|
| 16 |
+// "-tags safe" is added to the go build command line. The "disableunsafe" |
|
| 17 |
+// tag is deprecated and thus should not be used. |
|
| 18 |
+// +build js appengine safe disableunsafe |
|
| 19 |
+ |
|
| 20 |
+package spew |
|
| 21 |
+ |
|
| 22 |
+import "reflect" |
|
| 23 |
+ |
|
| 24 |
+const ( |
|
| 25 |
+ // UnsafeDisabled is a build-time constant which specifies whether or |
|
| 26 |
+ // not access to the unsafe package is available. |
|
| 27 |
+ UnsafeDisabled = true |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+// unsafeReflectValue typically converts the passed reflect.Value into a one |
|
| 31 |
+// that bypasses the typical safety restrictions preventing access to |
|
| 32 |
+// unaddressable and unexported data. However, doing this relies on access to |
|
| 33 |
+// the unsafe package. This is a stub version which simply returns the passed |
|
| 34 |
+// reflect.Value when the unsafe package is not available. |
|
| 35 |
+func unsafeReflectValue(v reflect.Value) reflect.Value {
|
|
| 36 |
+ return v |
|
| 37 |
+} |
| 0 | 38 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,341 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+package spew |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "bytes" |
|
| 20 |
+ "fmt" |
|
| 21 |
+ "io" |
|
| 22 |
+ "reflect" |
|
| 23 |
+ "sort" |
|
| 24 |
+ "strconv" |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+// Some constants in the form of bytes to avoid string overhead. This mirrors |
|
| 28 |
+// the technique used in the fmt package. |
|
| 29 |
+var ( |
|
| 30 |
+ panicBytes = []byte("(PANIC=")
|
|
| 31 |
+ plusBytes = []byte("+")
|
|
| 32 |
+ iBytes = []byte("i")
|
|
| 33 |
+ trueBytes = []byte("true")
|
|
| 34 |
+ falseBytes = []byte("false")
|
|
| 35 |
+ interfaceBytes = []byte("(interface {})")
|
|
| 36 |
+ commaNewlineBytes = []byte(",\n")
|
|
| 37 |
+ newlineBytes = []byte("\n")
|
|
| 38 |
+ openBraceBytes = []byte("{")
|
|
| 39 |
+ openBraceNewlineBytes = []byte("{\n")
|
|
| 40 |
+ closeBraceBytes = []byte("}")
|
|
| 41 |
+ asteriskBytes = []byte("*")
|
|
| 42 |
+ colonBytes = []byte(":")
|
|
| 43 |
+ colonSpaceBytes = []byte(": ")
|
|
| 44 |
+ openParenBytes = []byte("(")
|
|
| 45 |
+ closeParenBytes = []byte(")")
|
|
| 46 |
+ spaceBytes = []byte(" ")
|
|
| 47 |
+ pointerChainBytes = []byte("->")
|
|
| 48 |
+ nilAngleBytes = []byte("<nil>")
|
|
| 49 |
+ maxNewlineBytes = []byte("<max depth reached>\n")
|
|
| 50 |
+ maxShortBytes = []byte("<max>")
|
|
| 51 |
+ circularBytes = []byte("<already shown>")
|
|
| 52 |
+ circularShortBytes = []byte("<shown>")
|
|
| 53 |
+ invalidAngleBytes = []byte("<invalid>")
|
|
| 54 |
+ openBracketBytes = []byte("[")
|
|
| 55 |
+ closeBracketBytes = []byte("]")
|
|
| 56 |
+ percentBytes = []byte("%")
|
|
| 57 |
+ precisionBytes = []byte(".")
|
|
| 58 |
+ openAngleBytes = []byte("<")
|
|
| 59 |
+ closeAngleBytes = []byte(">")
|
|
| 60 |
+ openMapBytes = []byte("map[")
|
|
| 61 |
+ closeMapBytes = []byte("]")
|
|
| 62 |
+ lenEqualsBytes = []byte("len=")
|
|
| 63 |
+ capEqualsBytes = []byte("cap=")
|
|
| 64 |
+) |
|
| 65 |
+ |
|
| 66 |
+// hexDigits is used to map a decimal value to a hex digit. |
|
| 67 |
+var hexDigits = "0123456789abcdef" |
|
| 68 |
+ |
|
| 69 |
+// catchPanic handles any panics that might occur during the handleMethods |
|
| 70 |
+// calls. |
|
| 71 |
+func catchPanic(w io.Writer, v reflect.Value) {
|
|
| 72 |
+ if err := recover(); err != nil {
|
|
| 73 |
+ w.Write(panicBytes) |
|
| 74 |
+ fmt.Fprintf(w, "%v", err) |
|
| 75 |
+ w.Write(closeParenBytes) |
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// handleMethods attempts to call the Error and String methods on the underlying |
|
| 80 |
+// type the passed reflect.Value represents and outputes the result to Writer w. |
|
| 81 |
+// |
|
| 82 |
+// It handles panics in any called methods by catching and displaying the error |
|
| 83 |
+// as the formatted value. |
|
| 84 |
+func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
|
| 85 |
+ // We need an interface to check if the type implements the error or |
|
| 86 |
+ // Stringer interface. However, the reflect package won't give us an |
|
| 87 |
+ // interface on certain things like unexported struct fields in order |
|
| 88 |
+ // to enforce visibility rules. We use unsafe, when it's available, |
|
| 89 |
+ // to bypass these restrictions since this package does not mutate the |
|
| 90 |
+ // values. |
|
| 91 |
+ if !v.CanInterface() {
|
|
| 92 |
+ if UnsafeDisabled {
|
|
| 93 |
+ return false |
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ v = unsafeReflectValue(v) |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ // Choose whether or not to do error and Stringer interface lookups against |
|
| 100 |
+ // the base type or a pointer to the base type depending on settings. |
|
| 101 |
+ // Technically calling one of these methods with a pointer receiver can |
|
| 102 |
+ // mutate the value, however, types which choose to satisify an error or |
|
| 103 |
+ // Stringer interface with a pointer receiver should not be mutating their |
|
| 104 |
+ // state inside these interface methods. |
|
| 105 |
+ if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
|
| 106 |
+ v = unsafeReflectValue(v) |
|
| 107 |
+ } |
|
| 108 |
+ if v.CanAddr() {
|
|
| 109 |
+ v = v.Addr() |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ // Is it an error or Stringer? |
|
| 113 |
+ switch iface := v.Interface().(type) {
|
|
| 114 |
+ case error: |
|
| 115 |
+ defer catchPanic(w, v) |
|
| 116 |
+ if cs.ContinueOnMethod {
|
|
| 117 |
+ w.Write(openParenBytes) |
|
| 118 |
+ w.Write([]byte(iface.Error())) |
|
| 119 |
+ w.Write(closeParenBytes) |
|
| 120 |
+ w.Write(spaceBytes) |
|
| 121 |
+ return false |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ w.Write([]byte(iface.Error())) |
|
| 125 |
+ return true |
|
| 126 |
+ |
|
| 127 |
+ case fmt.Stringer: |
|
| 128 |
+ defer catchPanic(w, v) |
|
| 129 |
+ if cs.ContinueOnMethod {
|
|
| 130 |
+ w.Write(openParenBytes) |
|
| 131 |
+ w.Write([]byte(iface.String())) |
|
| 132 |
+ w.Write(closeParenBytes) |
|
| 133 |
+ w.Write(spaceBytes) |
|
| 134 |
+ return false |
|
| 135 |
+ } |
|
| 136 |
+ w.Write([]byte(iface.String())) |
|
| 137 |
+ return true |
|
| 138 |
+ } |
|
| 139 |
+ return false |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+// printBool outputs a boolean value as true or false to Writer w. |
|
| 143 |
+func printBool(w io.Writer, val bool) {
|
|
| 144 |
+ if val {
|
|
| 145 |
+ w.Write(trueBytes) |
|
| 146 |
+ } else {
|
|
| 147 |
+ w.Write(falseBytes) |
|
| 148 |
+ } |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+// printInt outputs a signed integer value to Writer w. |
|
| 152 |
+func printInt(w io.Writer, val int64, base int) {
|
|
| 153 |
+ w.Write([]byte(strconv.FormatInt(val, base))) |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+// printUint outputs an unsigned integer value to Writer w. |
|
| 157 |
+func printUint(w io.Writer, val uint64, base int) {
|
|
| 158 |
+ w.Write([]byte(strconv.FormatUint(val, base))) |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+// printFloat outputs a floating point value using the specified precision, |
|
| 162 |
+// which is expected to be 32 or 64bit, to Writer w. |
|
| 163 |
+func printFloat(w io.Writer, val float64, precision int) {
|
|
| 164 |
+ w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+// printComplex outputs a complex value using the specified float precision |
|
| 168 |
+// for the real and imaginary parts to Writer w. |
|
| 169 |
+func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
|
| 170 |
+ r := real(c) |
|
| 171 |
+ w.Write(openParenBytes) |
|
| 172 |
+ w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) |
|
| 173 |
+ i := imag(c) |
|
| 174 |
+ if i >= 0 {
|
|
| 175 |
+ w.Write(plusBytes) |
|
| 176 |
+ } |
|
| 177 |
+ w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) |
|
| 178 |
+ w.Write(iBytes) |
|
| 179 |
+ w.Write(closeParenBytes) |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' |
|
| 183 |
+// prefix to Writer w. |
|
| 184 |
+func printHexPtr(w io.Writer, p uintptr) {
|
|
| 185 |
+ // Null pointer. |
|
| 186 |
+ num := uint64(p) |
|
| 187 |
+ if num == 0 {
|
|
| 188 |
+ w.Write(nilAngleBytes) |
|
| 189 |
+ return |
|
| 190 |
+ } |
|
| 191 |
+ |
|
| 192 |
+ // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix |
|
| 193 |
+ buf := make([]byte, 18) |
|
| 194 |
+ |
|
| 195 |
+ // It's simpler to construct the hex string right to left. |
|
| 196 |
+ base := uint64(16) |
|
| 197 |
+ i := len(buf) - 1 |
|
| 198 |
+ for num >= base {
|
|
| 199 |
+ buf[i] = hexDigits[num%base] |
|
| 200 |
+ num /= base |
|
| 201 |
+ i-- |
|
| 202 |
+ } |
|
| 203 |
+ buf[i] = hexDigits[num] |
|
| 204 |
+ |
|
| 205 |
+ // Add '0x' prefix. |
|
| 206 |
+ i-- |
|
| 207 |
+ buf[i] = 'x' |
|
| 208 |
+ i-- |
|
| 209 |
+ buf[i] = '0' |
|
| 210 |
+ |
|
| 211 |
+ // Strip unused leading bytes. |
|
| 212 |
+ buf = buf[i:] |
|
| 213 |
+ w.Write(buf) |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+// valuesSorter implements sort.Interface to allow a slice of reflect.Value |
|
| 217 |
+// elements to be sorted. |
|
| 218 |
+type valuesSorter struct {
|
|
| 219 |
+ values []reflect.Value |
|
| 220 |
+ strings []string // either nil or same len and values |
|
| 221 |
+ cs *ConfigState |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+// newValuesSorter initializes a valuesSorter instance, which holds a set of |
|
| 225 |
+// surrogate keys on which the data should be sorted. It uses flags in |
|
| 226 |
+// ConfigState to decide if and how to populate those surrogate keys. |
|
| 227 |
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
|
| 228 |
+ vs := &valuesSorter{values: values, cs: cs}
|
|
| 229 |
+ if canSortSimply(vs.values[0].Kind()) {
|
|
| 230 |
+ return vs |
|
| 231 |
+ } |
|
| 232 |
+ if !cs.DisableMethods {
|
|
| 233 |
+ vs.strings = make([]string, len(values)) |
|
| 234 |
+ for i := range vs.values {
|
|
| 235 |
+ b := bytes.Buffer{}
|
|
| 236 |
+ if !handleMethods(cs, &b, vs.values[i]) {
|
|
| 237 |
+ vs.strings = nil |
|
| 238 |
+ break |
|
| 239 |
+ } |
|
| 240 |
+ vs.strings[i] = b.String() |
|
| 241 |
+ } |
|
| 242 |
+ } |
|
| 243 |
+ if vs.strings == nil && cs.SpewKeys {
|
|
| 244 |
+ vs.strings = make([]string, len(values)) |
|
| 245 |
+ for i := range vs.values {
|
|
| 246 |
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
|
| 247 |
+ } |
|
| 248 |
+ } |
|
| 249 |
+ return vs |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted |
|
| 253 |
+// directly, or whether it should be considered for sorting by surrogate keys |
|
| 254 |
+// (if the ConfigState allows it). |
|
| 255 |
+func canSortSimply(kind reflect.Kind) bool {
|
|
| 256 |
+ // This switch parallels valueSortLess, except for the default case. |
|
| 257 |
+ switch kind {
|
|
| 258 |
+ case reflect.Bool: |
|
| 259 |
+ return true |
|
| 260 |
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|
| 261 |
+ return true |
|
| 262 |
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|
| 263 |
+ return true |
|
| 264 |
+ case reflect.Float32, reflect.Float64: |
|
| 265 |
+ return true |
|
| 266 |
+ case reflect.String: |
|
| 267 |
+ return true |
|
| 268 |
+ case reflect.Uintptr: |
|
| 269 |
+ return true |
|
| 270 |
+ case reflect.Array: |
|
| 271 |
+ return true |
|
| 272 |
+ } |
|
| 273 |
+ return false |
|
| 274 |
+} |
|
| 275 |
+ |
|
| 276 |
+// Len returns the number of values in the slice. It is part of the |
|
| 277 |
+// sort.Interface implementation. |
|
| 278 |
+func (s *valuesSorter) Len() int {
|
|
| 279 |
+ return len(s.values) |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+// Swap swaps the values at the passed indices. It is part of the |
|
| 283 |
+// sort.Interface implementation. |
|
| 284 |
+func (s *valuesSorter) Swap(i, j int) {
|
|
| 285 |
+ s.values[i], s.values[j] = s.values[j], s.values[i] |
|
| 286 |
+ if s.strings != nil {
|
|
| 287 |
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i] |
|
| 288 |
+ } |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+// valueSortLess returns whether the first value should sort before the second |
|
| 292 |
+// value. It is used by valueSorter.Less as part of the sort.Interface |
|
| 293 |
+// implementation. |
|
| 294 |
+func valueSortLess(a, b reflect.Value) bool {
|
|
| 295 |
+ switch a.Kind() {
|
|
| 296 |
+ case reflect.Bool: |
|
| 297 |
+ return !a.Bool() && b.Bool() |
|
| 298 |
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|
| 299 |
+ return a.Int() < b.Int() |
|
| 300 |
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|
| 301 |
+ return a.Uint() < b.Uint() |
|
| 302 |
+ case reflect.Float32, reflect.Float64: |
|
| 303 |
+ return a.Float() < b.Float() |
|
| 304 |
+ case reflect.String: |
|
| 305 |
+ return a.String() < b.String() |
|
| 306 |
+ case reflect.Uintptr: |
|
| 307 |
+ return a.Uint() < b.Uint() |
|
| 308 |
+ case reflect.Array: |
|
| 309 |
+ // Compare the contents of both arrays. |
|
| 310 |
+ l := a.Len() |
|
| 311 |
+ for i := 0; i < l; i++ {
|
|
| 312 |
+ av := a.Index(i) |
|
| 313 |
+ bv := b.Index(i) |
|
| 314 |
+ if av.Interface() == bv.Interface() {
|
|
| 315 |
+ continue |
|
| 316 |
+ } |
|
| 317 |
+ return valueSortLess(av, bv) |
|
| 318 |
+ } |
|
| 319 |
+ } |
|
| 320 |
+ return a.String() < b.String() |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+// Less returns whether the value at index i should sort before the |
|
| 324 |
+// value at index j. It is part of the sort.Interface implementation. |
|
| 325 |
+func (s *valuesSorter) Less(i, j int) bool {
|
|
| 326 |
+ if s.strings == nil {
|
|
| 327 |
+ return valueSortLess(s.values[i], s.values[j]) |
|
| 328 |
+ } |
|
| 329 |
+ return s.strings[i] < s.strings[j] |
|
| 330 |
+} |
|
| 331 |
+ |
|
| 332 |
+// sortValues is a sort function that handles both native types and any type that |
|
| 333 |
+// can be converted to error or Stringer. Other inputs are sorted according to |
|
| 334 |
+// their Value.String() value to ensure display stability. |
|
| 335 |
+func sortValues(values []reflect.Value, cs *ConfigState) {
|
|
| 336 |
+ if len(values) == 0 {
|
|
| 337 |
+ return |
|
| 338 |
+ } |
|
| 339 |
+ sort.Sort(newValuesSorter(values, cs)) |
|
| 340 |
+} |
| 0 | 341 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,297 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+package spew |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "bytes" |
|
| 20 |
+ "fmt" |
|
| 21 |
+ "io" |
|
| 22 |
+ "os" |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+// ConfigState houses the configuration options used by spew to format and |
|
| 26 |
+// display values. There is a global instance, Config, that is used to control |
|
| 27 |
+// all top-level Formatter and Dump functionality. Each ConfigState instance |
|
| 28 |
+// provides methods equivalent to the top-level functions. |
|
| 29 |
+// |
|
| 30 |
+// The zero value for ConfigState provides no indentation. You would typically |
|
| 31 |
+// want to set it to a space or a tab. |
|
| 32 |
+// |
|
| 33 |
+// Alternatively, you can use NewDefaultConfig to get a ConfigState instance |
|
| 34 |
+// with default settings. See the documentation of NewDefaultConfig for default |
|
| 35 |
+// values. |
|
| 36 |
+type ConfigState struct {
|
|
| 37 |
+ // Indent specifies the string to use for each indentation level. The |
|
| 38 |
+ // global config instance that all top-level functions use set this to a |
|
| 39 |
+ // single space by default. If you would like more indentation, you might |
|
| 40 |
+ // set this to a tab with "\t" or perhaps two spaces with " ". |
|
| 41 |
+ Indent string |
|
| 42 |
+ |
|
| 43 |
+ // MaxDepth controls the maximum number of levels to descend into nested |
|
| 44 |
+ // data structures. The default, 0, means there is no limit. |
|
| 45 |
+ // |
|
| 46 |
+ // NOTE: Circular data structures are properly detected, so it is not |
|
| 47 |
+ // necessary to set this value unless you specifically want to limit deeply |
|
| 48 |
+ // nested data structures. |
|
| 49 |
+ MaxDepth int |
|
| 50 |
+ |
|
| 51 |
+ // DisableMethods specifies whether or not error and Stringer interfaces are |
|
| 52 |
+ // invoked for types that implement them. |
|
| 53 |
+ DisableMethods bool |
|
| 54 |
+ |
|
| 55 |
+ // DisablePointerMethods specifies whether or not to check for and invoke |
|
| 56 |
+ // error and Stringer interfaces on types which only accept a pointer |
|
| 57 |
+ // receiver when the current type is not a pointer. |
|
| 58 |
+ // |
|
| 59 |
+ // NOTE: This might be an unsafe action since calling one of these methods |
|
| 60 |
+ // with a pointer receiver could technically mutate the value, however, |
|
| 61 |
+ // in practice, types which choose to satisify an error or Stringer |
|
| 62 |
+ // interface with a pointer receiver should not be mutating their state |
|
| 63 |
+ // inside these interface methods. As a result, this option relies on |
|
| 64 |
+ // access to the unsafe package, so it will not have any effect when |
|
| 65 |
+ // running in environments without access to the unsafe package such as |
|
| 66 |
+ // Google App Engine or with the "safe" build tag specified. |
|
| 67 |
+ DisablePointerMethods bool |
|
| 68 |
+ |
|
| 69 |
+ // ContinueOnMethod specifies whether or not recursion should continue once |
|
| 70 |
+ // a custom error or Stringer interface is invoked. The default, false, |
|
| 71 |
+ // means it will print the results of invoking the custom error or Stringer |
|
| 72 |
+ // interface and return immediately instead of continuing to recurse into |
|
| 73 |
+ // the internals of the data type. |
|
| 74 |
+ // |
|
| 75 |
+ // NOTE: This flag does not have any effect if method invocation is disabled |
|
| 76 |
+ // via the DisableMethods or DisablePointerMethods options. |
|
| 77 |
+ ContinueOnMethod bool |
|
| 78 |
+ |
|
| 79 |
+ // SortKeys specifies map keys should be sorted before being printed. Use |
|
| 80 |
+ // this to have a more deterministic, diffable output. Note that only |
|
| 81 |
+ // native types (bool, int, uint, floats, uintptr and string) and types |
|
| 82 |
+ // that support the error or Stringer interfaces (if methods are |
|
| 83 |
+ // enabled) are supported, with other types sorted according to the |
|
| 84 |
+ // reflect.Value.String() output which guarantees display stability. |
|
| 85 |
+ SortKeys bool |
|
| 86 |
+ |
|
| 87 |
+ // SpewKeys specifies that, as a last resort attempt, map keys should |
|
| 88 |
+ // be spewed to strings and sorted by those strings. This is only |
|
| 89 |
+ // considered if SortKeys is true. |
|
| 90 |
+ SpewKeys bool |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+// Config is the active configuration of the top-level functions. |
|
| 94 |
+// The configuration can be changed by modifying the contents of spew.Config. |
|
| 95 |
+var Config = ConfigState{Indent: " "}
|
|
| 96 |
+ |
|
| 97 |
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were |
|
| 98 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 99 |
+// the formatted string as a value that satisfies error. See NewFormatter |
|
| 100 |
+// for formatting details. |
|
| 101 |
+// |
|
| 102 |
+// This function is shorthand for the following syntax: |
|
| 103 |
+// |
|
| 104 |
+// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 105 |
+func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
|
| 106 |
+ return fmt.Errorf(format, c.convertArgs(a)...) |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were |
|
| 110 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 111 |
+// the number of bytes written and any write error encountered. See |
|
| 112 |
+// NewFormatter for formatting details. |
|
| 113 |
+// |
|
| 114 |
+// This function is shorthand for the following syntax: |
|
| 115 |
+// |
|
| 116 |
+// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 117 |
+func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
|
| 118 |
+ return fmt.Fprint(w, c.convertArgs(a)...) |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were |
|
| 122 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 123 |
+// the number of bytes written and any write error encountered. See |
|
| 124 |
+// NewFormatter for formatting details. |
|
| 125 |
+// |
|
| 126 |
+// This function is shorthand for the following syntax: |
|
| 127 |
+// |
|
| 128 |
+// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 129 |
+func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
|
| 130 |
+ return fmt.Fprintf(w, format, c.convertArgs(a)...) |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it |
|
| 134 |
+// passed with a Formatter interface returned by c.NewFormatter. See |
|
| 135 |
+// NewFormatter for formatting details. |
|
| 136 |
+// |
|
| 137 |
+// This function is shorthand for the following syntax: |
|
| 138 |
+// |
|
| 139 |
+// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 140 |
+func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
|
| 141 |
+ return fmt.Fprintln(w, c.convertArgs(a)...) |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+// Print is a wrapper for fmt.Print that treats each argument as if it were |
|
| 145 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 146 |
+// the number of bytes written and any write error encountered. See |
|
| 147 |
+// NewFormatter for formatting details. |
|
| 148 |
+// |
|
| 149 |
+// This function is shorthand for the following syntax: |
|
| 150 |
+// |
|
| 151 |
+// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) |
|
| 152 |
+func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
|
| 153 |
+ return fmt.Print(c.convertArgs(a)...) |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were |
|
| 157 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 158 |
+// the number of bytes written and any write error encountered. See |
|
| 159 |
+// NewFormatter for formatting details. |
|
| 160 |
+// |
|
| 161 |
+// This function is shorthand for the following syntax: |
|
| 162 |
+// |
|
| 163 |
+// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 164 |
+func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
|
| 165 |
+ return fmt.Printf(format, c.convertArgs(a)...) |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+// Println is a wrapper for fmt.Println that treats each argument as if it were |
|
| 169 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 170 |
+// the number of bytes written and any write error encountered. See |
|
| 171 |
+// NewFormatter for formatting details. |
|
| 172 |
+// |
|
| 173 |
+// This function is shorthand for the following syntax: |
|
| 174 |
+// |
|
| 175 |
+// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) |
|
| 176 |
+func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
|
| 177 |
+ return fmt.Println(c.convertArgs(a)...) |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were |
|
| 181 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 182 |
+// the resulting string. See NewFormatter for formatting details. |
|
| 183 |
+// |
|
| 184 |
+// This function is shorthand for the following syntax: |
|
| 185 |
+// |
|
| 186 |
+// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) |
|
| 187 |
+func (c *ConfigState) Sprint(a ...interface{}) string {
|
|
| 188 |
+ return fmt.Sprint(c.convertArgs(a)...) |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 191 |
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were |
|
| 192 |
+// passed with a Formatter interface returned by c.NewFormatter. It returns |
|
| 193 |
+// the resulting string. See NewFormatter for formatting details. |
|
| 194 |
+// |
|
| 195 |
+// This function is shorthand for the following syntax: |
|
| 196 |
+// |
|
| 197 |
+// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) |
|
| 198 |
+func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
|
| 199 |
+ return fmt.Sprintf(format, c.convertArgs(a)...) |
|
| 200 |
+} |
|
| 201 |
+ |
|
| 202 |
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it |
|
| 203 |
+// were passed with a Formatter interface returned by c.NewFormatter. It |
|
| 204 |
+// returns the resulting string. See NewFormatter for formatting details. |
|
| 205 |
+// |
|
| 206 |
+// This function is shorthand for the following syntax: |
|
| 207 |
+// |
|
| 208 |
+// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) |
|
| 209 |
+func (c *ConfigState) Sprintln(a ...interface{}) string {
|
|
| 210 |
+ return fmt.Sprintln(c.convertArgs(a)...) |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+/* |
|
| 214 |
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter |
|
| 215 |
+interface. As a result, it integrates cleanly with standard fmt package |
|
| 216 |
+printing functions. The formatter is useful for inline printing of smaller data |
|
| 217 |
+types similar to the standard %v format specifier. |
|
| 218 |
+ |
|
| 219 |
+The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|
| 220 |
+addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb |
|
| 221 |
+combinations. Any other verbs such as %x and %q will be sent to the the |
|
| 222 |
+standard fmt package for formatting. In addition, the custom formatter ignores |
|
| 223 |
+the width and precision arguments (however they will still work on the format |
|
| 224 |
+specifiers not handled by the custom formatter). |
|
| 225 |
+ |
|
| 226 |
+Typically this function shouldn't be called directly. It is much easier to make |
|
| 227 |
+use of the custom formatter by calling one of the convenience functions such as |
|
| 228 |
+c.Printf, c.Println, or c.Printf. |
|
| 229 |
+*/ |
|
| 230 |
+func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
|
| 231 |
+ return newFormatter(c, v) |
|
| 232 |
+} |
|
| 233 |
+ |
|
| 234 |
+// Fdump formats and displays the passed arguments to io.Writer w. It formats |
|
| 235 |
+// exactly the same as Dump. |
|
| 236 |
+func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
|
| 237 |
+ fdump(c, w, a...) |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+/* |
|
| 241 |
+Dump displays the passed parameters to standard out with newlines, customizable |
|
| 242 |
+indentation, and additional debug information such as complete types and all |
|
| 243 |
+pointer addresses used to indirect to the final value. It provides the |
|
| 244 |
+following features over the built-in printing facilities provided by the fmt |
|
| 245 |
+package: |
|
| 246 |
+ |
|
| 247 |
+ * Pointers are dereferenced and followed |
|
| 248 |
+ * Circular data structures are detected and handled properly |
|
| 249 |
+ * Custom Stringer/error interfaces are optionally invoked, including |
|
| 250 |
+ on unexported types |
|
| 251 |
+ * Custom types which only implement the Stringer/error interfaces via |
|
| 252 |
+ a pointer receiver are optionally invoked when passing non-pointer |
|
| 253 |
+ variables |
|
| 254 |
+ * Byte arrays and slices are dumped like the hexdump -C command which |
|
| 255 |
+ includes offsets, byte values in hex, and ASCII output |
|
| 256 |
+ |
|
| 257 |
+The configuration options are controlled by modifying the public members |
|
| 258 |
+of c. See ConfigState for options documentation. |
|
| 259 |
+ |
|
| 260 |
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to |
|
| 261 |
+get the formatted result as a string. |
|
| 262 |
+*/ |
|
| 263 |
+func (c *ConfigState) Dump(a ...interface{}) {
|
|
| 264 |
+ fdump(c, os.Stdout, a...) |
|
| 265 |
+} |
|
| 266 |
+ |
|
| 267 |
+// Sdump returns a string with the passed arguments formatted exactly the same |
|
| 268 |
+// as Dump. |
|
| 269 |
+func (c *ConfigState) Sdump(a ...interface{}) string {
|
|
| 270 |
+ var buf bytes.Buffer |
|
| 271 |
+ fdump(c, &buf, a...) |
|
| 272 |
+ return buf.String() |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+// convertArgs accepts a slice of arguments and returns a slice of the same |
|
| 276 |
+// length with each argument converted to a spew Formatter interface using |
|
| 277 |
+// the ConfigState associated with s. |
|
| 278 |
+func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
|
| 279 |
+ formatters = make([]interface{}, len(args))
|
|
| 280 |
+ for index, arg := range args {
|
|
| 281 |
+ formatters[index] = newFormatter(c, arg) |
|
| 282 |
+ } |
|
| 283 |
+ return formatters |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+// NewDefaultConfig returns a ConfigState with the following default settings. |
|
| 287 |
+// |
|
| 288 |
+// Indent: " " |
|
| 289 |
+// MaxDepth: 0 |
|
| 290 |
+// DisableMethods: false |
|
| 291 |
+// DisablePointerMethods: false |
|
| 292 |
+// ContinueOnMethod: false |
|
| 293 |
+// SortKeys: false |
|
| 294 |
+func NewDefaultConfig() *ConfigState {
|
|
| 295 |
+ return &ConfigState{Indent: " "}
|
|
| 296 |
+} |
| 0 | 297 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,202 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+/* |
|
| 17 |
+Package spew implements a deep pretty printer for Go data structures to aid in |
|
| 18 |
+debugging. |
|
| 19 |
+ |
|
| 20 |
+A quick overview of the additional features spew provides over the built-in |
|
| 21 |
+printing facilities for Go data types are as follows: |
|
| 22 |
+ |
|
| 23 |
+ * Pointers are dereferenced and followed |
|
| 24 |
+ * Circular data structures are detected and handled properly |
|
| 25 |
+ * Custom Stringer/error interfaces are optionally invoked, including |
|
| 26 |
+ on unexported types |
|
| 27 |
+ * Custom types which only implement the Stringer/error interfaces via |
|
| 28 |
+ a pointer receiver are optionally invoked when passing non-pointer |
|
| 29 |
+ variables |
|
| 30 |
+ * Byte arrays and slices are dumped like the hexdump -C command which |
|
| 31 |
+ includes offsets, byte values in hex, and ASCII output (only when using |
|
| 32 |
+ Dump style) |
|
| 33 |
+ |
|
| 34 |
+There are two different approaches spew allows for dumping Go data structures: |
|
| 35 |
+ |
|
| 36 |
+ * Dump style which prints with newlines, customizable indentation, |
|
| 37 |
+ and additional debug information such as types and all pointer addresses |
|
| 38 |
+ used to indirect to the final value |
|
| 39 |
+ * A custom Formatter interface that integrates cleanly with the standard fmt |
|
| 40 |
+ package and replaces %v, %+v, %#v, and %#+v to provide inline printing |
|
| 41 |
+ similar to the default %v while providing the additional functionality |
|
| 42 |
+ outlined above and passing unsupported format verbs such as %x and %q |
|
| 43 |
+ along to fmt |
|
| 44 |
+ |
|
| 45 |
+Quick Start |
|
| 46 |
+ |
|
| 47 |
+This section demonstrates how to quickly get started with spew. See the |
|
| 48 |
+sections below for further details on formatting and configuration options. |
|
| 49 |
+ |
|
| 50 |
+To dump a variable with full newlines, indentation, type, and pointer |
|
| 51 |
+information use Dump, Fdump, or Sdump: |
|
| 52 |
+ spew.Dump(myVar1, myVar2, ...) |
|
| 53 |
+ spew.Fdump(someWriter, myVar1, myVar2, ...) |
|
| 54 |
+ str := spew.Sdump(myVar1, myVar2, ...) |
|
| 55 |
+ |
|
| 56 |
+Alternatively, if you would prefer to use format strings with a compacted inline |
|
| 57 |
+printing style, use the convenience wrappers Printf, Fprintf, etc with |
|
| 58 |
+%v (most compact), %+v (adds pointer addresses), %#v (adds types), or |
|
| 59 |
+%#+v (adds types and pointer addresses): |
|
| 60 |
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
| 61 |
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
| 62 |
+ spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|
| 63 |
+ spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|
| 64 |
+ |
|
| 65 |
+Configuration Options |
|
| 66 |
+ |
|
| 67 |
+Configuration of spew is handled by fields in the ConfigState type. For |
|
| 68 |
+convenience, all of the top-level functions use a global state available |
|
| 69 |
+via the spew.Config global. |
|
| 70 |
+ |
|
| 71 |
+It is also possible to create a ConfigState instance that provides methods |
|
| 72 |
+equivalent to the top-level functions. This allows concurrent configuration |
|
| 73 |
+options. See the ConfigState documentation for more details. |
|
| 74 |
+ |
|
| 75 |
+The following configuration options are available: |
|
| 76 |
+ * Indent |
|
| 77 |
+ String to use for each indentation level for Dump functions. |
|
| 78 |
+ It is a single space by default. A popular alternative is "\t". |
|
| 79 |
+ |
|
| 80 |
+ * MaxDepth |
|
| 81 |
+ Maximum number of levels to descend into nested data structures. |
|
| 82 |
+ There is no limit by default. |
|
| 83 |
+ |
|
| 84 |
+ * DisableMethods |
|
| 85 |
+ Disables invocation of error and Stringer interface methods. |
|
| 86 |
+ Method invocation is enabled by default. |
|
| 87 |
+ |
|
| 88 |
+ * DisablePointerMethods |
|
| 89 |
+ Disables invocation of error and Stringer interface methods on types |
|
| 90 |
+ which only accept pointer receivers from non-pointer variables. |
|
| 91 |
+ Pointer method invocation is enabled by default. |
|
| 92 |
+ |
|
| 93 |
+ * ContinueOnMethod |
|
| 94 |
+ Enables recursion into types after invoking error and Stringer interface |
|
| 95 |
+ methods. Recursion after method invocation is disabled by default. |
|
| 96 |
+ |
|
| 97 |
+ * SortKeys |
|
| 98 |
+ Specifies map keys should be sorted before being printed. Use |
|
| 99 |
+ this to have a more deterministic, diffable output. Note that |
|
| 100 |
+ only native types (bool, int, uint, floats, uintptr and string) |
|
| 101 |
+ and types which implement error or Stringer interfaces are |
|
| 102 |
+ supported with other types sorted according to the |
|
| 103 |
+ reflect.Value.String() output which guarantees display |
|
| 104 |
+ stability. Natural map order is used by default. |
|
| 105 |
+ |
|
| 106 |
+ * SpewKeys |
|
| 107 |
+ Specifies that, as a last resort attempt, map keys should be |
|
| 108 |
+ spewed to strings and sorted by those strings. This is only |
|
| 109 |
+ considered if SortKeys is true. |
|
| 110 |
+ |
|
| 111 |
+Dump Usage |
|
| 112 |
+ |
|
| 113 |
+Simply call spew.Dump with a list of variables you want to dump: |
|
| 114 |
+ |
|
| 115 |
+ spew.Dump(myVar1, myVar2, ...) |
|
| 116 |
+ |
|
| 117 |
+You may also call spew.Fdump if you would prefer to output to an arbitrary |
|
| 118 |
+io.Writer. For example, to dump to standard error: |
|
| 119 |
+ |
|
| 120 |
+ spew.Fdump(os.Stderr, myVar1, myVar2, ...) |
|
| 121 |
+ |
|
| 122 |
+A third option is to call spew.Sdump to get the formatted output as a string: |
|
| 123 |
+ |
|
| 124 |
+ str := spew.Sdump(myVar1, myVar2, ...) |
|
| 125 |
+ |
|
| 126 |
+Sample Dump Output |
|
| 127 |
+ |
|
| 128 |
+See the Dump example for details on the setup of the types and variables being |
|
| 129 |
+shown here. |
|
| 130 |
+ |
|
| 131 |
+ (main.Foo) {
|
|
| 132 |
+ unexportedField: (*main.Bar)(0xf84002e210)({
|
|
| 133 |
+ flag: (main.Flag) flagTwo, |
|
| 134 |
+ data: (uintptr) <nil> |
|
| 135 |
+ }), |
|
| 136 |
+ ExportedField: (map[interface {}]interface {}) (len=1) {
|
|
| 137 |
+ (string) (len=3) "one": (bool) true |
|
| 138 |
+ } |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C |
|
| 142 |
+command as shown. |
|
| 143 |
+ ([]uint8) (len=32 cap=32) {
|
|
| 144 |
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | |
|
| 145 |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| |
|
| 146 |
+ 00000020 31 32 |12| |
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+Custom Formatter |
|
| 150 |
+ |
|
| 151 |
+Spew provides a custom formatter that implements the fmt.Formatter interface |
|
| 152 |
+so that it integrates cleanly with standard fmt package printing functions. The |
|
| 153 |
+formatter is useful for inline printing of smaller data types similar to the |
|
| 154 |
+standard %v format specifier. |
|
| 155 |
+ |
|
| 156 |
+The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|
| 157 |
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb |
|
| 158 |
+combinations. Any other verbs such as %x and %q will be sent to the the |
|
| 159 |
+standard fmt package for formatting. In addition, the custom formatter ignores |
|
| 160 |
+the width and precision arguments (however they will still work on the format |
|
| 161 |
+specifiers not handled by the custom formatter). |
|
| 162 |
+ |
|
| 163 |
+Custom Formatter Usage |
|
| 164 |
+ |
|
| 165 |
+The simplest way to make use of the spew custom formatter is to call one of the |
|
| 166 |
+convenience functions such as spew.Printf, spew.Println, or spew.Printf. The |
|
| 167 |
+functions have syntax you are most likely already familiar with: |
|
| 168 |
+ |
|
| 169 |
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
| 170 |
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
| 171 |
+ spew.Println(myVar, myVar2) |
|
| 172 |
+ spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|
| 173 |
+ spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|
| 174 |
+ |
|
| 175 |
+See the Index for the full list convenience functions. |
|
| 176 |
+ |
|
| 177 |
+Sample Formatter Output |
|
| 178 |
+ |
|
| 179 |
+Double pointer to a uint8: |
|
| 180 |
+ %v: <**>5 |
|
| 181 |
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5 |
|
| 182 |
+ %#v: (**uint8)5 |
|
| 183 |
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 |
|
| 184 |
+ |
|
| 185 |
+Pointer to circular struct with a uint8 field and a pointer to itself: |
|
| 186 |
+ %v: <*>{1 <*><shown>}
|
|
| 187 |
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
|
| 188 |
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
|
| 189 |
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
|
| 190 |
+ |
|
| 191 |
+See the Printf example for details on the setup of variables being shown |
|
| 192 |
+here. |
|
| 193 |
+ |
|
| 194 |
+Errors |
|
| 195 |
+ |
|
| 196 |
+Since it is possible for custom Stringer/error interfaces to panic, spew |
|
| 197 |
+detects them and handles them internally by printing the panic information |
|
| 198 |
+inline with the output. Since spew is intended to provide deep pretty printing |
|
| 199 |
+capabilities on structures, it intentionally does not return any errors. |
|
| 200 |
+*/ |
|
| 201 |
+package spew |
| 0 | 202 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,509 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+package spew |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "bytes" |
|
| 20 |
+ "encoding/hex" |
|
| 21 |
+ "fmt" |
|
| 22 |
+ "io" |
|
| 23 |
+ "os" |
|
| 24 |
+ "reflect" |
|
| 25 |
+ "regexp" |
|
| 26 |
+ "strconv" |
|
| 27 |
+ "strings" |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+var ( |
|
| 31 |
+ // uint8Type is a reflect.Type representing a uint8. It is used to |
|
| 32 |
+ // convert cgo types to uint8 slices for hexdumping. |
|
| 33 |
+ uint8Type = reflect.TypeOf(uint8(0)) |
|
| 34 |
+ |
|
| 35 |
+ // cCharRE is a regular expression that matches a cgo char. |
|
| 36 |
+ // It is used to detect character arrays to hexdump them. |
|
| 37 |
+ cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
|
| 38 |
+ |
|
| 39 |
+ // cUnsignedCharRE is a regular expression that matches a cgo unsigned |
|
| 40 |
+ // char. It is used to detect unsigned character arrays to hexdump |
|
| 41 |
+ // them. |
|
| 42 |
+ cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
|
| 43 |
+ |
|
| 44 |
+ // cUint8tCharRE is a regular expression that matches a cgo uint8_t. |
|
| 45 |
+ // It is used to detect uint8_t arrays to hexdump them. |
|
| 46 |
+ cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
|
| 47 |
+) |
|
| 48 |
+ |
|
| 49 |
+// dumpState contains information about the state of a dump operation. |
|
| 50 |
+type dumpState struct {
|
|
| 51 |
+ w io.Writer |
|
| 52 |
+ depth int |
|
| 53 |
+ pointers map[uintptr]int |
|
| 54 |
+ ignoreNextType bool |
|
| 55 |
+ ignoreNextIndent bool |
|
| 56 |
+ cs *ConfigState |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// indent performs indentation according to the depth level and cs.Indent |
|
| 60 |
+// option. |
|
| 61 |
+func (d *dumpState) indent() {
|
|
| 62 |
+ if d.ignoreNextIndent {
|
|
| 63 |
+ d.ignoreNextIndent = false |
|
| 64 |
+ return |
|
| 65 |
+ } |
|
| 66 |
+ d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// unpackValue returns values inside of non-nil interfaces when possible. |
|
| 70 |
+// This is useful for data types like structs, arrays, slices, and maps which |
|
| 71 |
+// can contain varying types packed inside an interface. |
|
| 72 |
+func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
|
| 73 |
+ if v.Kind() == reflect.Interface && !v.IsNil() {
|
|
| 74 |
+ v = v.Elem() |
|
| 75 |
+ } |
|
| 76 |
+ return v |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// dumpPtr handles formatting of pointers by indirecting them as necessary. |
|
| 80 |
+func (d *dumpState) dumpPtr(v reflect.Value) {
|
|
| 81 |
+ // Remove pointers at or below the current depth from map used to detect |
|
| 82 |
+ // circular refs. |
|
| 83 |
+ for k, depth := range d.pointers {
|
|
| 84 |
+ if depth >= d.depth {
|
|
| 85 |
+ delete(d.pointers, k) |
|
| 86 |
+ } |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ // Keep list of all dereferenced pointers to show later. |
|
| 90 |
+ pointerChain := make([]uintptr, 0) |
|
| 91 |
+ |
|
| 92 |
+ // Figure out how many levels of indirection there are by dereferencing |
|
| 93 |
+ // pointers and unpacking interfaces down the chain while detecting circular |
|
| 94 |
+ // references. |
|
| 95 |
+ nilFound := false |
|
| 96 |
+ cycleFound := false |
|
| 97 |
+ indirects := 0 |
|
| 98 |
+ ve := v |
|
| 99 |
+ for ve.Kind() == reflect.Ptr {
|
|
| 100 |
+ if ve.IsNil() {
|
|
| 101 |
+ nilFound = true |
|
| 102 |
+ break |
|
| 103 |
+ } |
|
| 104 |
+ indirects++ |
|
| 105 |
+ addr := ve.Pointer() |
|
| 106 |
+ pointerChain = append(pointerChain, addr) |
|
| 107 |
+ if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
|
| 108 |
+ cycleFound = true |
|
| 109 |
+ indirects-- |
|
| 110 |
+ break |
|
| 111 |
+ } |
|
| 112 |
+ d.pointers[addr] = d.depth |
|
| 113 |
+ |
|
| 114 |
+ ve = ve.Elem() |
|
| 115 |
+ if ve.Kind() == reflect.Interface {
|
|
| 116 |
+ if ve.IsNil() {
|
|
| 117 |
+ nilFound = true |
|
| 118 |
+ break |
|
| 119 |
+ } |
|
| 120 |
+ ve = ve.Elem() |
|
| 121 |
+ } |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ // Display type information. |
|
| 125 |
+ d.w.Write(openParenBytes) |
|
| 126 |
+ d.w.Write(bytes.Repeat(asteriskBytes, indirects)) |
|
| 127 |
+ d.w.Write([]byte(ve.Type().String())) |
|
| 128 |
+ d.w.Write(closeParenBytes) |
|
| 129 |
+ |
|
| 130 |
+ // Display pointer information. |
|
| 131 |
+ if len(pointerChain) > 0 {
|
|
| 132 |
+ d.w.Write(openParenBytes) |
|
| 133 |
+ for i, addr := range pointerChain {
|
|
| 134 |
+ if i > 0 {
|
|
| 135 |
+ d.w.Write(pointerChainBytes) |
|
| 136 |
+ } |
|
| 137 |
+ printHexPtr(d.w, addr) |
|
| 138 |
+ } |
|
| 139 |
+ d.w.Write(closeParenBytes) |
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ // Display dereferenced value. |
|
| 143 |
+ d.w.Write(openParenBytes) |
|
| 144 |
+ switch {
|
|
| 145 |
+ case nilFound == true: |
|
| 146 |
+ d.w.Write(nilAngleBytes) |
|
| 147 |
+ |
|
| 148 |
+ case cycleFound == true: |
|
| 149 |
+ d.w.Write(circularBytes) |
|
| 150 |
+ |
|
| 151 |
+ default: |
|
| 152 |
+ d.ignoreNextType = true |
|
| 153 |
+ d.dump(ve) |
|
| 154 |
+ } |
|
| 155 |
+ d.w.Write(closeParenBytes) |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+// dumpSlice handles formatting of arrays and slices. Byte (uint8 under |
|
| 159 |
+// reflection) arrays and slices are dumped in hexdump -C fashion. |
|
| 160 |
+func (d *dumpState) dumpSlice(v reflect.Value) {
|
|
| 161 |
+ // Determine whether this type should be hex dumped or not. Also, |
|
| 162 |
+ // for types which should be hexdumped, try to use the underlying data |
|
| 163 |
+ // first, then fall back to trying to convert them to a uint8 slice. |
|
| 164 |
+ var buf []uint8 |
|
| 165 |
+ doConvert := false |
|
| 166 |
+ doHexDump := false |
|
| 167 |
+ numEntries := v.Len() |
|
| 168 |
+ if numEntries > 0 {
|
|
| 169 |
+ vt := v.Index(0).Type() |
|
| 170 |
+ vts := vt.String() |
|
| 171 |
+ switch {
|
|
| 172 |
+ // C types that need to be converted. |
|
| 173 |
+ case cCharRE.MatchString(vts): |
|
| 174 |
+ fallthrough |
|
| 175 |
+ case cUnsignedCharRE.MatchString(vts): |
|
| 176 |
+ fallthrough |
|
| 177 |
+ case cUint8tCharRE.MatchString(vts): |
|
| 178 |
+ doConvert = true |
|
| 179 |
+ |
|
| 180 |
+ // Try to use existing uint8 slices and fall back to converting |
|
| 181 |
+ // and copying if that fails. |
|
| 182 |
+ case vt.Kind() == reflect.Uint8: |
|
| 183 |
+ // We need an addressable interface to convert the type |
|
| 184 |
+ // to a byte slice. However, the reflect package won't |
|
| 185 |
+ // give us an interface on certain things like |
|
| 186 |
+ // unexported struct fields in order to enforce |
|
| 187 |
+ // visibility rules. We use unsafe, when available, to |
|
| 188 |
+ // bypass these restrictions since this package does not |
|
| 189 |
+ // mutate the values. |
|
| 190 |
+ vs := v |
|
| 191 |
+ if !vs.CanInterface() || !vs.CanAddr() {
|
|
| 192 |
+ vs = unsafeReflectValue(vs) |
|
| 193 |
+ } |
|
| 194 |
+ if !UnsafeDisabled {
|
|
| 195 |
+ vs = vs.Slice(0, numEntries) |
|
| 196 |
+ |
|
| 197 |
+ // Use the existing uint8 slice if it can be |
|
| 198 |
+ // type asserted. |
|
| 199 |
+ iface := vs.Interface() |
|
| 200 |
+ if slice, ok := iface.([]uint8); ok {
|
|
| 201 |
+ buf = slice |
|
| 202 |
+ doHexDump = true |
|
| 203 |
+ break |
|
| 204 |
+ } |
|
| 205 |
+ } |
|
| 206 |
+ |
|
| 207 |
+ // The underlying data needs to be converted if it can't |
|
| 208 |
+ // be type asserted to a uint8 slice. |
|
| 209 |
+ doConvert = true |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ // Copy and convert the underlying type if needed. |
|
| 213 |
+ if doConvert && vt.ConvertibleTo(uint8Type) {
|
|
| 214 |
+ // Convert and copy each element into a uint8 byte |
|
| 215 |
+ // slice. |
|
| 216 |
+ buf = make([]uint8, numEntries) |
|
| 217 |
+ for i := 0; i < numEntries; i++ {
|
|
| 218 |
+ vv := v.Index(i) |
|
| 219 |
+ buf[i] = uint8(vv.Convert(uint8Type).Uint()) |
|
| 220 |
+ } |
|
| 221 |
+ doHexDump = true |
|
| 222 |
+ } |
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ // Hexdump the entire slice as needed. |
|
| 226 |
+ if doHexDump {
|
|
| 227 |
+ indent := strings.Repeat(d.cs.Indent, d.depth) |
|
| 228 |
+ str := indent + hex.Dump(buf) |
|
| 229 |
+ str = strings.Replace(str, "\n", "\n"+indent, -1) |
|
| 230 |
+ str = strings.TrimRight(str, d.cs.Indent) |
|
| 231 |
+ d.w.Write([]byte(str)) |
|
| 232 |
+ return |
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 235 |
+ // Recursively call dump for each item. |
|
| 236 |
+ for i := 0; i < numEntries; i++ {
|
|
| 237 |
+ d.dump(d.unpackValue(v.Index(i))) |
|
| 238 |
+ if i < (numEntries - 1) {
|
|
| 239 |
+ d.w.Write(commaNewlineBytes) |
|
| 240 |
+ } else {
|
|
| 241 |
+ d.w.Write(newlineBytes) |
|
| 242 |
+ } |
|
| 243 |
+ } |
|
| 244 |
+} |
|
| 245 |
+ |
|
| 246 |
+// dump is the main workhorse for dumping a value. It uses the passed reflect |
|
| 247 |
+// value to figure out what kind of object we are dealing with and formats it |
|
| 248 |
+// appropriately. It is a recursive function, however circular data structures |
|
| 249 |
+// are detected and handled properly. |
|
| 250 |
+func (d *dumpState) dump(v reflect.Value) {
|
|
| 251 |
+ // Handle invalid reflect values immediately. |
|
| 252 |
+ kind := v.Kind() |
|
| 253 |
+ if kind == reflect.Invalid {
|
|
| 254 |
+ d.w.Write(invalidAngleBytes) |
|
| 255 |
+ return |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ // Handle pointers specially. |
|
| 259 |
+ if kind == reflect.Ptr {
|
|
| 260 |
+ d.indent() |
|
| 261 |
+ d.dumpPtr(v) |
|
| 262 |
+ return |
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ // Print type information unless already handled elsewhere. |
|
| 266 |
+ if !d.ignoreNextType {
|
|
| 267 |
+ d.indent() |
|
| 268 |
+ d.w.Write(openParenBytes) |
|
| 269 |
+ d.w.Write([]byte(v.Type().String())) |
|
| 270 |
+ d.w.Write(closeParenBytes) |
|
| 271 |
+ d.w.Write(spaceBytes) |
|
| 272 |
+ } |
|
| 273 |
+ d.ignoreNextType = false |
|
| 274 |
+ |
|
| 275 |
+ // Display length and capacity if the built-in len and cap functions |
|
| 276 |
+ // work with the value's kind and the len/cap itself is non-zero. |
|
| 277 |
+ valueLen, valueCap := 0, 0 |
|
| 278 |
+ switch v.Kind() {
|
|
| 279 |
+ case reflect.Array, reflect.Slice, reflect.Chan: |
|
| 280 |
+ valueLen, valueCap = v.Len(), v.Cap() |
|
| 281 |
+ case reflect.Map, reflect.String: |
|
| 282 |
+ valueLen = v.Len() |
|
| 283 |
+ } |
|
| 284 |
+ if valueLen != 0 || valueCap != 0 {
|
|
| 285 |
+ d.w.Write(openParenBytes) |
|
| 286 |
+ if valueLen != 0 {
|
|
| 287 |
+ d.w.Write(lenEqualsBytes) |
|
| 288 |
+ printInt(d.w, int64(valueLen), 10) |
|
| 289 |
+ } |
|
| 290 |
+ if valueCap != 0 {
|
|
| 291 |
+ if valueLen != 0 {
|
|
| 292 |
+ d.w.Write(spaceBytes) |
|
| 293 |
+ } |
|
| 294 |
+ d.w.Write(capEqualsBytes) |
|
| 295 |
+ printInt(d.w, int64(valueCap), 10) |
|
| 296 |
+ } |
|
| 297 |
+ d.w.Write(closeParenBytes) |
|
| 298 |
+ d.w.Write(spaceBytes) |
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ // Call Stringer/error interfaces if they exist and the handle methods flag |
|
| 302 |
+ // is enabled |
|
| 303 |
+ if !d.cs.DisableMethods {
|
|
| 304 |
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
|
| 305 |
+ if handled := handleMethods(d.cs, d.w, v); handled {
|
|
| 306 |
+ return |
|
| 307 |
+ } |
|
| 308 |
+ } |
|
| 309 |
+ } |
|
| 310 |
+ |
|
| 311 |
+ switch kind {
|
|
| 312 |
+ case reflect.Invalid: |
|
| 313 |
+ // Do nothing. We should never get here since invalid has already |
|
| 314 |
+ // been handled above. |
|
| 315 |
+ |
|
| 316 |
+ case reflect.Bool: |
|
| 317 |
+ printBool(d.w, v.Bool()) |
|
| 318 |
+ |
|
| 319 |
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|
| 320 |
+ printInt(d.w, v.Int(), 10) |
|
| 321 |
+ |
|
| 322 |
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|
| 323 |
+ printUint(d.w, v.Uint(), 10) |
|
| 324 |
+ |
|
| 325 |
+ case reflect.Float32: |
|
| 326 |
+ printFloat(d.w, v.Float(), 32) |
|
| 327 |
+ |
|
| 328 |
+ case reflect.Float64: |
|
| 329 |
+ printFloat(d.w, v.Float(), 64) |
|
| 330 |
+ |
|
| 331 |
+ case reflect.Complex64: |
|
| 332 |
+ printComplex(d.w, v.Complex(), 32) |
|
| 333 |
+ |
|
| 334 |
+ case reflect.Complex128: |
|
| 335 |
+ printComplex(d.w, v.Complex(), 64) |
|
| 336 |
+ |
|
| 337 |
+ case reflect.Slice: |
|
| 338 |
+ if v.IsNil() {
|
|
| 339 |
+ d.w.Write(nilAngleBytes) |
|
| 340 |
+ break |
|
| 341 |
+ } |
|
| 342 |
+ fallthrough |
|
| 343 |
+ |
|
| 344 |
+ case reflect.Array: |
|
| 345 |
+ d.w.Write(openBraceNewlineBytes) |
|
| 346 |
+ d.depth++ |
|
| 347 |
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
| 348 |
+ d.indent() |
|
| 349 |
+ d.w.Write(maxNewlineBytes) |
|
| 350 |
+ } else {
|
|
| 351 |
+ d.dumpSlice(v) |
|
| 352 |
+ } |
|
| 353 |
+ d.depth-- |
|
| 354 |
+ d.indent() |
|
| 355 |
+ d.w.Write(closeBraceBytes) |
|
| 356 |
+ |
|
| 357 |
+ case reflect.String: |
|
| 358 |
+ d.w.Write([]byte(strconv.Quote(v.String()))) |
|
| 359 |
+ |
|
| 360 |
+ case reflect.Interface: |
|
| 361 |
+ // The only time we should get here is for nil interfaces due to |
|
| 362 |
+ // unpackValue calls. |
|
| 363 |
+ if v.IsNil() {
|
|
| 364 |
+ d.w.Write(nilAngleBytes) |
|
| 365 |
+ } |
|
| 366 |
+ |
|
| 367 |
+ case reflect.Ptr: |
|
| 368 |
+ // Do nothing. We should never get here since pointers have already |
|
| 369 |
+ // been handled above. |
|
| 370 |
+ |
|
| 371 |
+ case reflect.Map: |
|
| 372 |
+ // nil maps should be indicated as different than empty maps |
|
| 373 |
+ if v.IsNil() {
|
|
| 374 |
+ d.w.Write(nilAngleBytes) |
|
| 375 |
+ break |
|
| 376 |
+ } |
|
| 377 |
+ |
|
| 378 |
+ d.w.Write(openBraceNewlineBytes) |
|
| 379 |
+ d.depth++ |
|
| 380 |
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
| 381 |
+ d.indent() |
|
| 382 |
+ d.w.Write(maxNewlineBytes) |
|
| 383 |
+ } else {
|
|
| 384 |
+ numEntries := v.Len() |
|
| 385 |
+ keys := v.MapKeys() |
|
| 386 |
+ if d.cs.SortKeys {
|
|
| 387 |
+ sortValues(keys, d.cs) |
|
| 388 |
+ } |
|
| 389 |
+ for i, key := range keys {
|
|
| 390 |
+ d.dump(d.unpackValue(key)) |
|
| 391 |
+ d.w.Write(colonSpaceBytes) |
|
| 392 |
+ d.ignoreNextIndent = true |
|
| 393 |
+ d.dump(d.unpackValue(v.MapIndex(key))) |
|
| 394 |
+ if i < (numEntries - 1) {
|
|
| 395 |
+ d.w.Write(commaNewlineBytes) |
|
| 396 |
+ } else {
|
|
| 397 |
+ d.w.Write(newlineBytes) |
|
| 398 |
+ } |
|
| 399 |
+ } |
|
| 400 |
+ } |
|
| 401 |
+ d.depth-- |
|
| 402 |
+ d.indent() |
|
| 403 |
+ d.w.Write(closeBraceBytes) |
|
| 404 |
+ |
|
| 405 |
+ case reflect.Struct: |
|
| 406 |
+ d.w.Write(openBraceNewlineBytes) |
|
| 407 |
+ d.depth++ |
|
| 408 |
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
| 409 |
+ d.indent() |
|
| 410 |
+ d.w.Write(maxNewlineBytes) |
|
| 411 |
+ } else {
|
|
| 412 |
+ vt := v.Type() |
|
| 413 |
+ numFields := v.NumField() |
|
| 414 |
+ for i := 0; i < numFields; i++ {
|
|
| 415 |
+ d.indent() |
|
| 416 |
+ vtf := vt.Field(i) |
|
| 417 |
+ d.w.Write([]byte(vtf.Name)) |
|
| 418 |
+ d.w.Write(colonSpaceBytes) |
|
| 419 |
+ d.ignoreNextIndent = true |
|
| 420 |
+ d.dump(d.unpackValue(v.Field(i))) |
|
| 421 |
+ if i < (numFields - 1) {
|
|
| 422 |
+ d.w.Write(commaNewlineBytes) |
|
| 423 |
+ } else {
|
|
| 424 |
+ d.w.Write(newlineBytes) |
|
| 425 |
+ } |
|
| 426 |
+ } |
|
| 427 |
+ } |
|
| 428 |
+ d.depth-- |
|
| 429 |
+ d.indent() |
|
| 430 |
+ d.w.Write(closeBraceBytes) |
|
| 431 |
+ |
|
| 432 |
+ case reflect.Uintptr: |
|
| 433 |
+ printHexPtr(d.w, uintptr(v.Uint())) |
|
| 434 |
+ |
|
| 435 |
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func: |
|
| 436 |
+ printHexPtr(d.w, v.Pointer()) |
|
| 437 |
+ |
|
| 438 |
+ // There were not any other types at the time this code was written, but |
|
| 439 |
+ // fall back to letting the default fmt package handle it in case any new |
|
| 440 |
+ // types are added. |
|
| 441 |
+ default: |
|
| 442 |
+ if v.CanInterface() {
|
|
| 443 |
+ fmt.Fprintf(d.w, "%v", v.Interface()) |
|
| 444 |
+ } else {
|
|
| 445 |
+ fmt.Fprintf(d.w, "%v", v.String()) |
|
| 446 |
+ } |
|
| 447 |
+ } |
|
| 448 |
+} |
|
| 449 |
+ |
|
| 450 |
+// fdump is a helper function to consolidate the logic from the various public |
|
| 451 |
+// methods which take varying writers and config states. |
|
| 452 |
+func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
|
| 453 |
+ for _, arg := range a {
|
|
| 454 |
+ if arg == nil {
|
|
| 455 |
+ w.Write(interfaceBytes) |
|
| 456 |
+ w.Write(spaceBytes) |
|
| 457 |
+ w.Write(nilAngleBytes) |
|
| 458 |
+ w.Write(newlineBytes) |
|
| 459 |
+ continue |
|
| 460 |
+ } |
|
| 461 |
+ |
|
| 462 |
+ d := dumpState{w: w, cs: cs}
|
|
| 463 |
+ d.pointers = make(map[uintptr]int) |
|
| 464 |
+ d.dump(reflect.ValueOf(arg)) |
|
| 465 |
+ d.w.Write(newlineBytes) |
|
| 466 |
+ } |
|
| 467 |
+} |
|
| 468 |
+ |
|
| 469 |
+// Fdump formats and displays the passed arguments to io.Writer w. It formats |
|
| 470 |
+// exactly the same as Dump. |
|
| 471 |
+func Fdump(w io.Writer, a ...interface{}) {
|
|
| 472 |
+ fdump(&Config, w, a...) |
|
| 473 |
+} |
|
| 474 |
+ |
|
| 475 |
+// Sdump returns a string with the passed arguments formatted exactly the same |
|
| 476 |
+// as Dump. |
|
| 477 |
+func Sdump(a ...interface{}) string {
|
|
| 478 |
+ var buf bytes.Buffer |
|
| 479 |
+ fdump(&Config, &buf, a...) |
|
| 480 |
+ return buf.String() |
|
| 481 |
+} |
|
| 482 |
+ |
|
| 483 |
+/* |
|
| 484 |
+Dump displays the passed parameters to standard out with newlines, customizable |
|
| 485 |
+indentation, and additional debug information such as complete types and all |
|
| 486 |
+pointer addresses used to indirect to the final value. It provides the |
|
| 487 |
+following features over the built-in printing facilities provided by the fmt |
|
| 488 |
+package: |
|
| 489 |
+ |
|
| 490 |
+ * Pointers are dereferenced and followed |
|
| 491 |
+ * Circular data structures are detected and handled properly |
|
| 492 |
+ * Custom Stringer/error interfaces are optionally invoked, including |
|
| 493 |
+ on unexported types |
|
| 494 |
+ * Custom types which only implement the Stringer/error interfaces via |
|
| 495 |
+ a pointer receiver are optionally invoked when passing non-pointer |
|
| 496 |
+ variables |
|
| 497 |
+ * Byte arrays and slices are dumped like the hexdump -C command which |
|
| 498 |
+ includes offsets, byte values in hex, and ASCII output |
|
| 499 |
+ |
|
| 500 |
+The configuration options are controlled by an exported package global, |
|
| 501 |
+spew.Config. See ConfigState for options documentation. |
|
| 502 |
+ |
|
| 503 |
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to |
|
| 504 |
+get the formatted result as a string. |
|
| 505 |
+*/ |
|
| 506 |
+func Dump(a ...interface{}) {
|
|
| 507 |
+ fdump(&Config, os.Stdout, a...) |
|
| 508 |
+} |
| 0 | 509 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,419 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+package spew |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "bytes" |
|
| 20 |
+ "fmt" |
|
| 21 |
+ "reflect" |
|
| 22 |
+ "strconv" |
|
| 23 |
+ "strings" |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+// supportedFlags is a list of all the character flags supported by fmt package. |
|
| 27 |
+const supportedFlags = "0-+# " |
|
| 28 |
+ |
|
| 29 |
+// formatState implements the fmt.Formatter interface and contains information |
|
| 30 |
+// about the state of a formatting operation. The NewFormatter function can |
|
| 31 |
+// be used to get a new Formatter which can be used directly as arguments |
|
| 32 |
+// in standard fmt package printing calls. |
|
| 33 |
+type formatState struct {
|
|
| 34 |
+ value interface{}
|
|
| 35 |
+ fs fmt.State |
|
| 36 |
+ depth int |
|
| 37 |
+ pointers map[uintptr]int |
|
| 38 |
+ ignoreNextType bool |
|
| 39 |
+ cs *ConfigState |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// buildDefaultFormat recreates the original format string without precision |
|
| 43 |
+// and width information to pass in to fmt.Sprintf in the case of an |
|
| 44 |
+// unrecognized type. Unless new types are added to the language, this |
|
| 45 |
+// function won't ever be called. |
|
| 46 |
+func (f *formatState) buildDefaultFormat() (format string) {
|
|
| 47 |
+ buf := bytes.NewBuffer(percentBytes) |
|
| 48 |
+ |
|
| 49 |
+ for _, flag := range supportedFlags {
|
|
| 50 |
+ if f.fs.Flag(int(flag)) {
|
|
| 51 |
+ buf.WriteRune(flag) |
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ buf.WriteRune('v')
|
|
| 56 |
+ |
|
| 57 |
+ format = buf.String() |
|
| 58 |
+ return format |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// constructOrigFormat recreates the original format string including precision |
|
| 62 |
+// and width information to pass along to the standard fmt package. This allows |
|
| 63 |
+// automatic deferral of all format strings this package doesn't support. |
|
| 64 |
+func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
|
| 65 |
+ buf := bytes.NewBuffer(percentBytes) |
|
| 66 |
+ |
|
| 67 |
+ for _, flag := range supportedFlags {
|
|
| 68 |
+ if f.fs.Flag(int(flag)) {
|
|
| 69 |
+ buf.WriteRune(flag) |
|
| 70 |
+ } |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ if width, ok := f.fs.Width(); ok {
|
|
| 74 |
+ buf.WriteString(strconv.Itoa(width)) |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ if precision, ok := f.fs.Precision(); ok {
|
|
| 78 |
+ buf.Write(precisionBytes) |
|
| 79 |
+ buf.WriteString(strconv.Itoa(precision)) |
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 82 |
+ buf.WriteRune(verb) |
|
| 83 |
+ |
|
| 84 |
+ format = buf.String() |
|
| 85 |
+ return format |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+// unpackValue returns values inside of non-nil interfaces when possible and |
|
| 89 |
+// ensures that types for values which have been unpacked from an interface |
|
| 90 |
+// are displayed when the show types flag is also set. |
|
| 91 |
+// This is useful for data types like structs, arrays, slices, and maps which |
|
| 92 |
+// can contain varying types packed inside an interface. |
|
| 93 |
+func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
|
| 94 |
+ if v.Kind() == reflect.Interface {
|
|
| 95 |
+ f.ignoreNextType = false |
|
| 96 |
+ if !v.IsNil() {
|
|
| 97 |
+ v = v.Elem() |
|
| 98 |
+ } |
|
| 99 |
+ } |
|
| 100 |
+ return v |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// formatPtr handles formatting of pointers by indirecting them as necessary. |
|
| 104 |
+func (f *formatState) formatPtr(v reflect.Value) {
|
|
| 105 |
+ // Display nil if top level pointer is nil. |
|
| 106 |
+ showTypes := f.fs.Flag('#')
|
|
| 107 |
+ if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
|
| 108 |
+ f.fs.Write(nilAngleBytes) |
|
| 109 |
+ return |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ // Remove pointers at or below the current depth from map used to detect |
|
| 113 |
+ // circular refs. |
|
| 114 |
+ for k, depth := range f.pointers {
|
|
| 115 |
+ if depth >= f.depth {
|
|
| 116 |
+ delete(f.pointers, k) |
|
| 117 |
+ } |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ // Keep list of all dereferenced pointers to possibly show later. |
|
| 121 |
+ pointerChain := make([]uintptr, 0) |
|
| 122 |
+ |
|
| 123 |
+ // Figure out how many levels of indirection there are by derferencing |
|
| 124 |
+ // pointers and unpacking interfaces down the chain while detecting circular |
|
| 125 |
+ // references. |
|
| 126 |
+ nilFound := false |
|
| 127 |
+ cycleFound := false |
|
| 128 |
+ indirects := 0 |
|
| 129 |
+ ve := v |
|
| 130 |
+ for ve.Kind() == reflect.Ptr {
|
|
| 131 |
+ if ve.IsNil() {
|
|
| 132 |
+ nilFound = true |
|
| 133 |
+ break |
|
| 134 |
+ } |
|
| 135 |
+ indirects++ |
|
| 136 |
+ addr := ve.Pointer() |
|
| 137 |
+ pointerChain = append(pointerChain, addr) |
|
| 138 |
+ if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
|
| 139 |
+ cycleFound = true |
|
| 140 |
+ indirects-- |
|
| 141 |
+ break |
|
| 142 |
+ } |
|
| 143 |
+ f.pointers[addr] = f.depth |
|
| 144 |
+ |
|
| 145 |
+ ve = ve.Elem() |
|
| 146 |
+ if ve.Kind() == reflect.Interface {
|
|
| 147 |
+ if ve.IsNil() {
|
|
| 148 |
+ nilFound = true |
|
| 149 |
+ break |
|
| 150 |
+ } |
|
| 151 |
+ ve = ve.Elem() |
|
| 152 |
+ } |
|
| 153 |
+ } |
|
| 154 |
+ |
|
| 155 |
+ // Display type or indirection level depending on flags. |
|
| 156 |
+ if showTypes && !f.ignoreNextType {
|
|
| 157 |
+ f.fs.Write(openParenBytes) |
|
| 158 |
+ f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) |
|
| 159 |
+ f.fs.Write([]byte(ve.Type().String())) |
|
| 160 |
+ f.fs.Write(closeParenBytes) |
|
| 161 |
+ } else {
|
|
| 162 |
+ if nilFound || cycleFound {
|
|
| 163 |
+ indirects += strings.Count(ve.Type().String(), "*") |
|
| 164 |
+ } |
|
| 165 |
+ f.fs.Write(openAngleBytes) |
|
| 166 |
+ f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
|
| 167 |
+ f.fs.Write(closeAngleBytes) |
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ // Display pointer information depending on flags. |
|
| 171 |
+ if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
|
| 172 |
+ f.fs.Write(openParenBytes) |
|
| 173 |
+ for i, addr := range pointerChain {
|
|
| 174 |
+ if i > 0 {
|
|
| 175 |
+ f.fs.Write(pointerChainBytes) |
|
| 176 |
+ } |
|
| 177 |
+ printHexPtr(f.fs, addr) |
|
| 178 |
+ } |
|
| 179 |
+ f.fs.Write(closeParenBytes) |
|
| 180 |
+ } |
|
| 181 |
+ |
|
| 182 |
+ // Display dereferenced value. |
|
| 183 |
+ switch {
|
|
| 184 |
+ case nilFound == true: |
|
| 185 |
+ f.fs.Write(nilAngleBytes) |
|
| 186 |
+ |
|
| 187 |
+ case cycleFound == true: |
|
| 188 |
+ f.fs.Write(circularShortBytes) |
|
| 189 |
+ |
|
| 190 |
+ default: |
|
| 191 |
+ f.ignoreNextType = true |
|
| 192 |
+ f.format(ve) |
|
| 193 |
+ } |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+// format is the main workhorse for providing the Formatter interface. It |
|
| 197 |
+// uses the passed reflect value to figure out what kind of object we are |
|
| 198 |
+// dealing with and formats it appropriately. It is a recursive function, |
|
| 199 |
+// however circular data structures are detected and handled properly. |
|
| 200 |
+func (f *formatState) format(v reflect.Value) {
|
|
| 201 |
+ // Handle invalid reflect values immediately. |
|
| 202 |
+ kind := v.Kind() |
|
| 203 |
+ if kind == reflect.Invalid {
|
|
| 204 |
+ f.fs.Write(invalidAngleBytes) |
|
| 205 |
+ return |
|
| 206 |
+ } |
|
| 207 |
+ |
|
| 208 |
+ // Handle pointers specially. |
|
| 209 |
+ if kind == reflect.Ptr {
|
|
| 210 |
+ f.formatPtr(v) |
|
| 211 |
+ return |
|
| 212 |
+ } |
|
| 213 |
+ |
|
| 214 |
+ // Print type information unless already handled elsewhere. |
|
| 215 |
+ if !f.ignoreNextType && f.fs.Flag('#') {
|
|
| 216 |
+ f.fs.Write(openParenBytes) |
|
| 217 |
+ f.fs.Write([]byte(v.Type().String())) |
|
| 218 |
+ f.fs.Write(closeParenBytes) |
|
| 219 |
+ } |
|
| 220 |
+ f.ignoreNextType = false |
|
| 221 |
+ |
|
| 222 |
+ // Call Stringer/error interfaces if they exist and the handle methods |
|
| 223 |
+ // flag is enabled. |
|
| 224 |
+ if !f.cs.DisableMethods {
|
|
| 225 |
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
|
| 226 |
+ if handled := handleMethods(f.cs, f.fs, v); handled {
|
|
| 227 |
+ return |
|
| 228 |
+ } |
|
| 229 |
+ } |
|
| 230 |
+ } |
|
| 231 |
+ |
|
| 232 |
+ switch kind {
|
|
| 233 |
+ case reflect.Invalid: |
|
| 234 |
+ // Do nothing. We should never get here since invalid has already |
|
| 235 |
+ // been handled above. |
|
| 236 |
+ |
|
| 237 |
+ case reflect.Bool: |
|
| 238 |
+ printBool(f.fs, v.Bool()) |
|
| 239 |
+ |
|
| 240 |
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|
| 241 |
+ printInt(f.fs, v.Int(), 10) |
|
| 242 |
+ |
|
| 243 |
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|
| 244 |
+ printUint(f.fs, v.Uint(), 10) |
|
| 245 |
+ |
|
| 246 |
+ case reflect.Float32: |
|
| 247 |
+ printFloat(f.fs, v.Float(), 32) |
|
| 248 |
+ |
|
| 249 |
+ case reflect.Float64: |
|
| 250 |
+ printFloat(f.fs, v.Float(), 64) |
|
| 251 |
+ |
|
| 252 |
+ case reflect.Complex64: |
|
| 253 |
+ printComplex(f.fs, v.Complex(), 32) |
|
| 254 |
+ |
|
| 255 |
+ case reflect.Complex128: |
|
| 256 |
+ printComplex(f.fs, v.Complex(), 64) |
|
| 257 |
+ |
|
| 258 |
+ case reflect.Slice: |
|
| 259 |
+ if v.IsNil() {
|
|
| 260 |
+ f.fs.Write(nilAngleBytes) |
|
| 261 |
+ break |
|
| 262 |
+ } |
|
| 263 |
+ fallthrough |
|
| 264 |
+ |
|
| 265 |
+ case reflect.Array: |
|
| 266 |
+ f.fs.Write(openBracketBytes) |
|
| 267 |
+ f.depth++ |
|
| 268 |
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
| 269 |
+ f.fs.Write(maxShortBytes) |
|
| 270 |
+ } else {
|
|
| 271 |
+ numEntries := v.Len() |
|
| 272 |
+ for i := 0; i < numEntries; i++ {
|
|
| 273 |
+ if i > 0 {
|
|
| 274 |
+ f.fs.Write(spaceBytes) |
|
| 275 |
+ } |
|
| 276 |
+ f.ignoreNextType = true |
|
| 277 |
+ f.format(f.unpackValue(v.Index(i))) |
|
| 278 |
+ } |
|
| 279 |
+ } |
|
| 280 |
+ f.depth-- |
|
| 281 |
+ f.fs.Write(closeBracketBytes) |
|
| 282 |
+ |
|
| 283 |
+ case reflect.String: |
|
| 284 |
+ f.fs.Write([]byte(v.String())) |
|
| 285 |
+ |
|
| 286 |
+ case reflect.Interface: |
|
| 287 |
+ // The only time we should get here is for nil interfaces due to |
|
| 288 |
+ // unpackValue calls. |
|
| 289 |
+ if v.IsNil() {
|
|
| 290 |
+ f.fs.Write(nilAngleBytes) |
|
| 291 |
+ } |
|
| 292 |
+ |
|
| 293 |
+ case reflect.Ptr: |
|
| 294 |
+ // Do nothing. We should never get here since pointers have already |
|
| 295 |
+ // been handled above. |
|
| 296 |
+ |
|
| 297 |
+ case reflect.Map: |
|
| 298 |
+ // nil maps should be indicated as different than empty maps |
|
| 299 |
+ if v.IsNil() {
|
|
| 300 |
+ f.fs.Write(nilAngleBytes) |
|
| 301 |
+ break |
|
| 302 |
+ } |
|
| 303 |
+ |
|
| 304 |
+ f.fs.Write(openMapBytes) |
|
| 305 |
+ f.depth++ |
|
| 306 |
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
| 307 |
+ f.fs.Write(maxShortBytes) |
|
| 308 |
+ } else {
|
|
| 309 |
+ keys := v.MapKeys() |
|
| 310 |
+ if f.cs.SortKeys {
|
|
| 311 |
+ sortValues(keys, f.cs) |
|
| 312 |
+ } |
|
| 313 |
+ for i, key := range keys {
|
|
| 314 |
+ if i > 0 {
|
|
| 315 |
+ f.fs.Write(spaceBytes) |
|
| 316 |
+ } |
|
| 317 |
+ f.ignoreNextType = true |
|
| 318 |
+ f.format(f.unpackValue(key)) |
|
| 319 |
+ f.fs.Write(colonBytes) |
|
| 320 |
+ f.ignoreNextType = true |
|
| 321 |
+ f.format(f.unpackValue(v.MapIndex(key))) |
|
| 322 |
+ } |
|
| 323 |
+ } |
|
| 324 |
+ f.depth-- |
|
| 325 |
+ f.fs.Write(closeMapBytes) |
|
| 326 |
+ |
|
| 327 |
+ case reflect.Struct: |
|
| 328 |
+ numFields := v.NumField() |
|
| 329 |
+ f.fs.Write(openBraceBytes) |
|
| 330 |
+ f.depth++ |
|
| 331 |
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
| 332 |
+ f.fs.Write(maxShortBytes) |
|
| 333 |
+ } else {
|
|
| 334 |
+ vt := v.Type() |
|
| 335 |
+ for i := 0; i < numFields; i++ {
|
|
| 336 |
+ if i > 0 {
|
|
| 337 |
+ f.fs.Write(spaceBytes) |
|
| 338 |
+ } |
|
| 339 |
+ vtf := vt.Field(i) |
|
| 340 |
+ if f.fs.Flag('+') || f.fs.Flag('#') {
|
|
| 341 |
+ f.fs.Write([]byte(vtf.Name)) |
|
| 342 |
+ f.fs.Write(colonBytes) |
|
| 343 |
+ } |
|
| 344 |
+ f.format(f.unpackValue(v.Field(i))) |
|
| 345 |
+ } |
|
| 346 |
+ } |
|
| 347 |
+ f.depth-- |
|
| 348 |
+ f.fs.Write(closeBraceBytes) |
|
| 349 |
+ |
|
| 350 |
+ case reflect.Uintptr: |
|
| 351 |
+ printHexPtr(f.fs, uintptr(v.Uint())) |
|
| 352 |
+ |
|
| 353 |
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func: |
|
| 354 |
+ printHexPtr(f.fs, v.Pointer()) |
|
| 355 |
+ |
|
| 356 |
+ // There were not any other types at the time this code was written, but |
|
| 357 |
+ // fall back to letting the default fmt package handle it if any get added. |
|
| 358 |
+ default: |
|
| 359 |
+ format := f.buildDefaultFormat() |
|
| 360 |
+ if v.CanInterface() {
|
|
| 361 |
+ fmt.Fprintf(f.fs, format, v.Interface()) |
|
| 362 |
+ } else {
|
|
| 363 |
+ fmt.Fprintf(f.fs, format, v.String()) |
|
| 364 |
+ } |
|
| 365 |
+ } |
|
| 366 |
+} |
|
| 367 |
+ |
|
| 368 |
+// Format satisfies the fmt.Formatter interface. See NewFormatter for usage |
|
| 369 |
+// details. |
|
| 370 |
+func (f *formatState) Format(fs fmt.State, verb rune) {
|
|
| 371 |
+ f.fs = fs |
|
| 372 |
+ |
|
| 373 |
+ // Use standard formatting for verbs that are not v. |
|
| 374 |
+ if verb != 'v' {
|
|
| 375 |
+ format := f.constructOrigFormat(verb) |
|
| 376 |
+ fmt.Fprintf(fs, format, f.value) |
|
| 377 |
+ return |
|
| 378 |
+ } |
|
| 379 |
+ |
|
| 380 |
+ if f.value == nil {
|
|
| 381 |
+ if fs.Flag('#') {
|
|
| 382 |
+ fs.Write(interfaceBytes) |
|
| 383 |
+ } |
|
| 384 |
+ fs.Write(nilAngleBytes) |
|
| 385 |
+ return |
|
| 386 |
+ } |
|
| 387 |
+ |
|
| 388 |
+ f.format(reflect.ValueOf(f.value)) |
|
| 389 |
+} |
|
| 390 |
+ |
|
| 391 |
+// newFormatter is a helper function to consolidate the logic from the various |
|
| 392 |
+// public methods which take varying config states. |
|
| 393 |
+func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
|
| 394 |
+ fs := &formatState{value: v, cs: cs}
|
|
| 395 |
+ fs.pointers = make(map[uintptr]int) |
|
| 396 |
+ return fs |
|
| 397 |
+} |
|
| 398 |
+ |
|
| 399 |
+/* |
|
| 400 |
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter |
|
| 401 |
+interface. As a result, it integrates cleanly with standard fmt package |
|
| 402 |
+printing functions. The formatter is useful for inline printing of smaller data |
|
| 403 |
+types similar to the standard %v format specifier. |
|
| 404 |
+ |
|
| 405 |
+The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|
| 406 |
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb |
|
| 407 |
+combinations. Any other verbs such as %x and %q will be sent to the the |
|
| 408 |
+standard fmt package for formatting. In addition, the custom formatter ignores |
|
| 409 |
+the width and precision arguments (however they will still work on the format |
|
| 410 |
+specifiers not handled by the custom formatter). |
|
| 411 |
+ |
|
| 412 |
+Typically this function shouldn't be called directly. It is much easier to make |
|
| 413 |
+use of the custom formatter by calling one of the convenience functions such as |
|
| 414 |
+Printf, Println, or Fprintf. |
|
| 415 |
+*/ |
|
| 416 |
+func NewFormatter(v interface{}) fmt.Formatter {
|
|
| 417 |
+ return newFormatter(&Config, v) |
|
| 418 |
+} |
| 0 | 419 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,148 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) 2013 Dave Collins <dave@davec.name> |
|
| 2 |
+ * |
|
| 3 |
+ * Permission to use, copy, modify, and distribute this software for any |
|
| 4 |
+ * purpose with or without fee is hereby granted, provided that the above |
|
| 5 |
+ * copyright notice and this permission notice appear in all copies. |
|
| 6 |
+ * |
|
| 7 |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
| 8 |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
| 9 |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
| 10 |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
| 11 |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
| 12 |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
| 13 |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
| 14 |
+ */ |
|
| 15 |
+ |
|
| 16 |
+package spew |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "fmt" |
|
| 20 |
+ "io" |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were |
|
| 24 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 25 |
+// returns the formatted string as a value that satisfies error. See |
|
| 26 |
+// NewFormatter for formatting details. |
|
| 27 |
+// |
|
| 28 |
+// This function is shorthand for the following syntax: |
|
| 29 |
+// |
|
| 30 |
+// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 31 |
+func Errorf(format string, a ...interface{}) (err error) {
|
|
| 32 |
+ return fmt.Errorf(format, convertArgs(a)...) |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were |
|
| 36 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 37 |
+// returns the number of bytes written and any write error encountered. See |
|
| 38 |
+// NewFormatter for formatting details. |
|
| 39 |
+// |
|
| 40 |
+// This function is shorthand for the following syntax: |
|
| 41 |
+// |
|
| 42 |
+// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 43 |
+func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
|
| 44 |
+ return fmt.Fprint(w, convertArgs(a)...) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were |
|
| 48 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 49 |
+// returns the number of bytes written and any write error encountered. See |
|
| 50 |
+// NewFormatter for formatting details. |
|
| 51 |
+// |
|
| 52 |
+// This function is shorthand for the following syntax: |
|
| 53 |
+// |
|
| 54 |
+// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 55 |
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
|
| 56 |
+ return fmt.Fprintf(w, format, convertArgs(a)...) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it |
|
| 60 |
+// passed with a default Formatter interface returned by NewFormatter. See |
|
| 61 |
+// NewFormatter for formatting details. |
|
| 62 |
+// |
|
| 63 |
+// This function is shorthand for the following syntax: |
|
| 64 |
+// |
|
| 65 |
+// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 66 |
+func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
|
| 67 |
+ return fmt.Fprintln(w, convertArgs(a)...) |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// Print is a wrapper for fmt.Print that treats each argument as if it were |
|
| 71 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 72 |
+// returns the number of bytes written and any write error encountered. See |
|
| 73 |
+// NewFormatter for formatting details. |
|
| 74 |
+// |
|
| 75 |
+// This function is shorthand for the following syntax: |
|
| 76 |
+// |
|
| 77 |
+// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 78 |
+func Print(a ...interface{}) (n int, err error) {
|
|
| 79 |
+ return fmt.Print(convertArgs(a)...) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were |
|
| 83 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 84 |
+// returns the number of bytes written and any write error encountered. See |
|
| 85 |
+// NewFormatter for formatting details. |
|
| 86 |
+// |
|
| 87 |
+// This function is shorthand for the following syntax: |
|
| 88 |
+// |
|
| 89 |
+// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 90 |
+func Printf(format string, a ...interface{}) (n int, err error) {
|
|
| 91 |
+ return fmt.Printf(format, convertArgs(a)...) |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// Println is a wrapper for fmt.Println that treats each argument as if it were |
|
| 95 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 96 |
+// returns the number of bytes written and any write error encountered. See |
|
| 97 |
+// NewFormatter for formatting details. |
|
| 98 |
+// |
|
| 99 |
+// This function is shorthand for the following syntax: |
|
| 100 |
+// |
|
| 101 |
+// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 102 |
+func Println(a ...interface{}) (n int, err error) {
|
|
| 103 |
+ return fmt.Println(convertArgs(a)...) |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were |
|
| 107 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 108 |
+// returns the resulting string. See NewFormatter for formatting details. |
|
| 109 |
+// |
|
| 110 |
+// This function is shorthand for the following syntax: |
|
| 111 |
+// |
|
| 112 |
+// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 113 |
+func Sprint(a ...interface{}) string {
|
|
| 114 |
+ return fmt.Sprint(convertArgs(a)...) |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were |
|
| 118 |
+// passed with a default Formatter interface returned by NewFormatter. It |
|
| 119 |
+// returns the resulting string. See NewFormatter for formatting details. |
|
| 120 |
+// |
|
| 121 |
+// This function is shorthand for the following syntax: |
|
| 122 |
+// |
|
| 123 |
+// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 124 |
+func Sprintf(format string, a ...interface{}) string {
|
|
| 125 |
+ return fmt.Sprintf(format, convertArgs(a)...) |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it |
|
| 129 |
+// were passed with a default Formatter interface returned by NewFormatter. It |
|
| 130 |
+// returns the resulting string. See NewFormatter for formatting details. |
|
| 131 |
+// |
|
| 132 |
+// This function is shorthand for the following syntax: |
|
| 133 |
+// |
|
| 134 |
+// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) |
|
| 135 |
+func Sprintln(a ...interface{}) string {
|
|
| 136 |
+ return fmt.Sprintln(convertArgs(a)...) |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+// convertArgs accepts a slice of arguments and returns a slice of the same |
|
| 140 |
+// length with each argument converted to a default spew Formatter interface. |
|
| 141 |
+func convertArgs(args []interface{}) (formatters []interface{}) {
|
|
| 142 |
+ formatters = make([]interface{}, len(args))
|
|
| 143 |
+ for index, arg := range args {
|
|
| 144 |
+ formatters[index] = NewFormatter(arg) |
|
| 145 |
+ } |
|
| 146 |
+ return formatters |
|
| 147 |
+} |