mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-06 09:06:44 +00:00
![Paddy](/assets/img/avatar_default.png)
Switch to using Go modules. This migrates our vendor.json to use Go 1.11's modules system, and replaces the vendor folder with the output of go mod vendor. The vendored code should remain basically the same; I believe some tree shaking of packages and support scripts/licenses/READMEs/etc. happened. This also fixes Travis and our Makefile to no longer use govendor.
510 lines
14 KiB
Go
510 lines
14 KiB
Go
/*
|
|
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
package spew
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// uint8Type is a reflect.Type representing a uint8. It is used to
|
|
// convert cgo types to uint8 slices for hexdumping.
|
|
uint8Type = reflect.TypeOf(uint8(0))
|
|
|
|
// cCharRE is a regular expression that matches a cgo char.
|
|
// It is used to detect character arrays to hexdump them.
|
|
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
|
|
|
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
|
// char. It is used to detect unsigned character arrays to hexdump
|
|
// them.
|
|
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
|
|
|
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
|
// It is used to detect uint8_t arrays to hexdump them.
|
|
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
|
)
|
|
|
|
// dumpState contains information about the state of a dump operation.
|
|
type dumpState struct {
|
|
w io.Writer
|
|
depth int
|
|
pointers map[uintptr]int
|
|
ignoreNextType bool
|
|
ignoreNextIndent bool
|
|
cs *ConfigState
|
|
}
|
|
|
|
// indent performs indentation according to the depth level and cs.Indent
|
|
// option.
|
|
func (d *dumpState) indent() {
|
|
if d.ignoreNextIndent {
|
|
d.ignoreNextIndent = false
|
|
return
|
|
}
|
|
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
|
}
|
|
|
|
// unpackValue returns values inside of non-nil interfaces when possible.
|
|
// This is useful for data types like structs, arrays, slices, and maps which
|
|
// can contain varying types packed inside an interface.
|
|
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
|
v = v.Elem()
|
|
}
|
|
return v
|
|
}
|
|
|
|
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
|
func (d *dumpState) dumpPtr(v reflect.Value) {
|
|
// Remove pointers at or below the current depth from map used to detect
|
|
// circular refs.
|
|
for k, depth := range d.pointers {
|
|
if depth >= d.depth {
|
|
delete(d.pointers, k)
|
|
}
|
|
}
|
|
|
|
// Keep list of all dereferenced pointers to show later.
|
|
pointerChain := make([]uintptr, 0)
|
|
|
|
// Figure out how many levels of indirection there are by dereferencing
|
|
// pointers and unpacking interfaces down the chain while detecting circular
|
|
// references.
|
|
nilFound := false
|
|
cycleFound := false
|
|
indirects := 0
|
|
ve := v
|
|
for ve.Kind() == reflect.Ptr {
|
|
if ve.IsNil() {
|
|
nilFound = true
|
|
break
|
|
}
|
|
indirects++
|
|
addr := ve.Pointer()
|
|
pointerChain = append(pointerChain, addr)
|
|
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
|
cycleFound = true
|
|
indirects--
|
|
break
|
|
}
|
|
d.pointers[addr] = d.depth
|
|
|
|
ve = ve.Elem()
|
|
if ve.Kind() == reflect.Interface {
|
|
if ve.IsNil() {
|
|
nilFound = true
|
|
break
|
|
}
|
|
ve = ve.Elem()
|
|
}
|
|
}
|
|
|
|
// Display type information.
|
|
d.w.Write(openParenBytes)
|
|
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
|
d.w.Write([]byte(ve.Type().String()))
|
|
d.w.Write(closeParenBytes)
|
|
|
|
// Display pointer information.
|
|
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
|
d.w.Write(openParenBytes)
|
|
for i, addr := range pointerChain {
|
|
if i > 0 {
|
|
d.w.Write(pointerChainBytes)
|
|
}
|
|
printHexPtr(d.w, addr)
|
|
}
|
|
d.w.Write(closeParenBytes)
|
|
}
|
|
|
|
// Display dereferenced value.
|
|
d.w.Write(openParenBytes)
|
|
switch {
|
|
case nilFound:
|
|
d.w.Write(nilAngleBytes)
|
|
|
|
case cycleFound:
|
|
d.w.Write(circularBytes)
|
|
|
|
default:
|
|
d.ignoreNextType = true
|
|
d.dump(ve)
|
|
}
|
|
d.w.Write(closeParenBytes)
|
|
}
|
|
|
|
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
|
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
|
func (d *dumpState) dumpSlice(v reflect.Value) {
|
|
// Determine whether this type should be hex dumped or not. Also,
|
|
// for types which should be hexdumped, try to use the underlying data
|
|
// first, then fall back to trying to convert them to a uint8 slice.
|
|
var buf []uint8
|
|
doConvert := false
|
|
doHexDump := false
|
|
numEntries := v.Len()
|
|
if numEntries > 0 {
|
|
vt := v.Index(0).Type()
|
|
vts := vt.String()
|
|
switch {
|
|
// C types that need to be converted.
|
|
case cCharRE.MatchString(vts):
|
|
fallthrough
|
|
case cUnsignedCharRE.MatchString(vts):
|
|
fallthrough
|
|
case cUint8tCharRE.MatchString(vts):
|
|
doConvert = true
|
|
|
|
// Try to use existing uint8 slices and fall back to converting
|
|
// and copying if that fails.
|
|
case vt.Kind() == reflect.Uint8:
|
|
// We need an addressable interface to convert the type
|
|
// to a byte slice. However, the reflect package won't
|
|
// give us an interface on certain things like
|
|
// unexported struct fields in order to enforce
|
|
// visibility rules. We use unsafe, when available, to
|
|
// bypass these restrictions since this package does not
|
|
// mutate the values.
|
|
vs := v
|
|
if !vs.CanInterface() || !vs.CanAddr() {
|
|
vs = unsafeReflectValue(vs)
|
|
}
|
|
if !UnsafeDisabled {
|
|
vs = vs.Slice(0, numEntries)
|
|
|
|
// Use the existing uint8 slice if it can be
|
|
// type asserted.
|
|
iface := vs.Interface()
|
|
if slice, ok := iface.([]uint8); ok {
|
|
buf = slice
|
|
doHexDump = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// The underlying data needs to be converted if it can't
|
|
// be type asserted to a uint8 slice.
|
|
doConvert = true
|
|
}
|
|
|
|
// Copy and convert the underlying type if needed.
|
|
if doConvert && vt.ConvertibleTo(uint8Type) {
|
|
// Convert and copy each element into a uint8 byte
|
|
// slice.
|
|
buf = make([]uint8, numEntries)
|
|
for i := 0; i < numEntries; i++ {
|
|
vv := v.Index(i)
|
|
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
|
}
|
|
doHexDump = true
|
|
}
|
|
}
|
|
|
|
// Hexdump the entire slice as needed.
|
|
if doHexDump {
|
|
indent := strings.Repeat(d.cs.Indent, d.depth)
|
|
str := indent + hex.Dump(buf)
|
|
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
|
str = strings.TrimRight(str, d.cs.Indent)
|
|
d.w.Write([]byte(str))
|
|
return
|
|
}
|
|
|
|
// Recursively call dump for each item.
|
|
for i := 0; i < numEntries; i++ {
|
|
d.dump(d.unpackValue(v.Index(i)))
|
|
if i < (numEntries - 1) {
|
|
d.w.Write(commaNewlineBytes)
|
|
} else {
|
|
d.w.Write(newlineBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
|
// value to figure out what kind of object we are dealing with and formats it
|
|
// appropriately. It is a recursive function, however circular data structures
|
|
// are detected and handled properly.
|
|
func (d *dumpState) dump(v reflect.Value) {
|
|
// Handle invalid reflect values immediately.
|
|
kind := v.Kind()
|
|
if kind == reflect.Invalid {
|
|
d.w.Write(invalidAngleBytes)
|
|
return
|
|
}
|
|
|
|
// Handle pointers specially.
|
|
if kind == reflect.Ptr {
|
|
d.indent()
|
|
d.dumpPtr(v)
|
|
return
|
|
}
|
|
|
|
// Print type information unless already handled elsewhere.
|
|
if !d.ignoreNextType {
|
|
d.indent()
|
|
d.w.Write(openParenBytes)
|
|
d.w.Write([]byte(v.Type().String()))
|
|
d.w.Write(closeParenBytes)
|
|
d.w.Write(spaceBytes)
|
|
}
|
|
d.ignoreNextType = false
|
|
|
|
// Display length and capacity if the built-in len and cap functions
|
|
// work with the value's kind and the len/cap itself is non-zero.
|
|
valueLen, valueCap := 0, 0
|
|
switch v.Kind() {
|
|
case reflect.Array, reflect.Slice, reflect.Chan:
|
|
valueLen, valueCap = v.Len(), v.Cap()
|
|
case reflect.Map, reflect.String:
|
|
valueLen = v.Len()
|
|
}
|
|
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
|
d.w.Write(openParenBytes)
|
|
if valueLen != 0 {
|
|
d.w.Write(lenEqualsBytes)
|
|
printInt(d.w, int64(valueLen), 10)
|
|
}
|
|
if !d.cs.DisableCapacities && valueCap != 0 {
|
|
if valueLen != 0 {
|
|
d.w.Write(spaceBytes)
|
|
}
|
|
d.w.Write(capEqualsBytes)
|
|
printInt(d.w, int64(valueCap), 10)
|
|
}
|
|
d.w.Write(closeParenBytes)
|
|
d.w.Write(spaceBytes)
|
|
}
|
|
|
|
// Call Stringer/error interfaces if they exist and the handle methods flag
|
|
// is enabled
|
|
if !d.cs.DisableMethods {
|
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
|
if handled := handleMethods(d.cs, d.w, v); handled {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
switch kind {
|
|
case reflect.Invalid:
|
|
// Do nothing. We should never get here since invalid has already
|
|
// been handled above.
|
|
|
|
case reflect.Bool:
|
|
printBool(d.w, v.Bool())
|
|
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
printInt(d.w, v.Int(), 10)
|
|
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
printUint(d.w, v.Uint(), 10)
|
|
|
|
case reflect.Float32:
|
|
printFloat(d.w, v.Float(), 32)
|
|
|
|
case reflect.Float64:
|
|
printFloat(d.w, v.Float(), 64)
|
|
|
|
case reflect.Complex64:
|
|
printComplex(d.w, v.Complex(), 32)
|
|
|
|
case reflect.Complex128:
|
|
printComplex(d.w, v.Complex(), 64)
|
|
|
|
case reflect.Slice:
|
|
if v.IsNil() {
|
|
d.w.Write(nilAngleBytes)
|
|
break
|
|
}
|
|
fallthrough
|
|
|
|
case reflect.Array:
|
|
d.w.Write(openBraceNewlineBytes)
|
|
d.depth++
|
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
d.indent()
|
|
d.w.Write(maxNewlineBytes)
|
|
} else {
|
|
d.dumpSlice(v)
|
|
}
|
|
d.depth--
|
|
d.indent()
|
|
d.w.Write(closeBraceBytes)
|
|
|
|
case reflect.String:
|
|
d.w.Write([]byte(strconv.Quote(v.String())))
|
|
|
|
case reflect.Interface:
|
|
// The only time we should get here is for nil interfaces due to
|
|
// unpackValue calls.
|
|
if v.IsNil() {
|
|
d.w.Write(nilAngleBytes)
|
|
}
|
|
|
|
case reflect.Ptr:
|
|
// Do nothing. We should never get here since pointers have already
|
|
// been handled above.
|
|
|
|
case reflect.Map:
|
|
// nil maps should be indicated as different than empty maps
|
|
if v.IsNil() {
|
|
d.w.Write(nilAngleBytes)
|
|
break
|
|
}
|
|
|
|
d.w.Write(openBraceNewlineBytes)
|
|
d.depth++
|
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
d.indent()
|
|
d.w.Write(maxNewlineBytes)
|
|
} else {
|
|
numEntries := v.Len()
|
|
keys := v.MapKeys()
|
|
if d.cs.SortKeys {
|
|
sortValues(keys, d.cs)
|
|
}
|
|
for i, key := range keys {
|
|
d.dump(d.unpackValue(key))
|
|
d.w.Write(colonSpaceBytes)
|
|
d.ignoreNextIndent = true
|
|
d.dump(d.unpackValue(v.MapIndex(key)))
|
|
if i < (numEntries - 1) {
|
|
d.w.Write(commaNewlineBytes)
|
|
} else {
|
|
d.w.Write(newlineBytes)
|
|
}
|
|
}
|
|
}
|
|
d.depth--
|
|
d.indent()
|
|
d.w.Write(closeBraceBytes)
|
|
|
|
case reflect.Struct:
|
|
d.w.Write(openBraceNewlineBytes)
|
|
d.depth++
|
|
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
d.indent()
|
|
d.w.Write(maxNewlineBytes)
|
|
} else {
|
|
vt := v.Type()
|
|
numFields := v.NumField()
|
|
for i := 0; i < numFields; i++ {
|
|
d.indent()
|
|
vtf := vt.Field(i)
|
|
d.w.Write([]byte(vtf.Name))
|
|
d.w.Write(colonSpaceBytes)
|
|
d.ignoreNextIndent = true
|
|
d.dump(d.unpackValue(v.Field(i)))
|
|
if i < (numFields - 1) {
|
|
d.w.Write(commaNewlineBytes)
|
|
} else {
|
|
d.w.Write(newlineBytes)
|
|
}
|
|
}
|
|
}
|
|
d.depth--
|
|
d.indent()
|
|
d.w.Write(closeBraceBytes)
|
|
|
|
case reflect.Uintptr:
|
|
printHexPtr(d.w, uintptr(v.Uint()))
|
|
|
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
|
printHexPtr(d.w, v.Pointer())
|
|
|
|
// There were not any other types at the time this code was written, but
|
|
// fall back to letting the default fmt package handle it in case any new
|
|
// types are added.
|
|
default:
|
|
if v.CanInterface() {
|
|
fmt.Fprintf(d.w, "%v", v.Interface())
|
|
} else {
|
|
fmt.Fprintf(d.w, "%v", v.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
// fdump is a helper function to consolidate the logic from the various public
|
|
// methods which take varying writers and config states.
|
|
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
|
for _, arg := range a {
|
|
if arg == nil {
|
|
w.Write(interfaceBytes)
|
|
w.Write(spaceBytes)
|
|
w.Write(nilAngleBytes)
|
|
w.Write(newlineBytes)
|
|
continue
|
|
}
|
|
|
|
d := dumpState{w: w, cs: cs}
|
|
d.pointers = make(map[uintptr]int)
|
|
d.dump(reflect.ValueOf(arg))
|
|
d.w.Write(newlineBytes)
|
|
}
|
|
}
|
|
|
|
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
|
// exactly the same as Dump.
|
|
func Fdump(w io.Writer, a ...interface{}) {
|
|
fdump(&Config, w, a...)
|
|
}
|
|
|
|
// Sdump returns a string with the passed arguments formatted exactly the same
|
|
// as Dump.
|
|
func Sdump(a ...interface{}) string {
|
|
var buf bytes.Buffer
|
|
fdump(&Config, &buf, a...)
|
|
return buf.String()
|
|
}
|
|
|
|
/*
|
|
Dump displays the passed parameters to standard out with newlines, customizable
|
|
indentation, and additional debug information such as complete types and all
|
|
pointer addresses used to indirect to the final value. It provides the
|
|
following features over the built-in printing facilities provided by the fmt
|
|
package:
|
|
|
|
* Pointers are dereferenced and followed
|
|
* Circular data structures are detected and handled properly
|
|
* Custom Stringer/error interfaces are optionally invoked, including
|
|
on unexported types
|
|
* Custom types which only implement the Stringer/error interfaces via
|
|
a pointer receiver are optionally invoked when passing non-pointer
|
|
variables
|
|
* Byte arrays and slices are dumped like the hexdump -C command which
|
|
includes offsets, byte values in hex, and ASCII output
|
|
|
|
The configuration options are controlled by an exported package global,
|
|
spew.Config. See ConfigState for options documentation.
|
|
|
|
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
|
get the formatted result as a string.
|
|
*/
|
|
func Dump(a ...interface{}) {
|
|
fdump(&Config, os.Stdout, a...)
|
|
}
|