summaryrefslogtreecommitdiffstats
path: root/vendor
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-26 10:34:56 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-01-26 10:58:33 +1100
commitce3bcfe37cf0c68f501fb09d543e9e212b0eaa61 (patch)
tree74487a25e696ff6e9230ae46d0803bffc0179231 /vendor
parentf4ddf2f0d4be4ccc7efacca2ba81e4a6d46b6318 (diff)
fix reflog failing to properly refresh
Diffstat (limited to 'vendor')
-rw-r--r--vendor/github.com/sanity-io/litter/.gitignore1
-rw-r--r--vendor/github.com/sanity-io/litter/CHANGELOG.md7
-rw-r--r--vendor/github.com/sanity-io/litter/LICENSE21
-rw-r--r--vendor/github.com/sanity-io/litter/README.md202
-rw-r--r--vendor/github.com/sanity-io/litter/dump.go528
-rw-r--r--vendor/github.com/sanity-io/litter/go.mod9
-rw-r--r--vendor/github.com/sanity-io/litter/go.sum6
-rw-r--r--vendor/github.com/sanity-io/litter/pointers.go167
-rw-r--r--vendor/github.com/sanity-io/litter/print.go50
-rw-r--r--vendor/github.com/sanity-io/litter/util.go28
-rw-r--r--vendor/modules.txt3
11 files changed, 1022 insertions, 0 deletions
diff --git a/vendor/github.com/sanity-io/litter/.gitignore b/vendor/github.com/sanity-io/litter/.gitignore
new file mode 100644
index 000000000..e43b0f988
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/vendor/github.com/sanity-io/litter/CHANGELOG.md b/vendor/github.com/sanity-io/litter/CHANGELOG.md
new file mode 100644
index 000000000..0be671fbc
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/CHANGELOG.md
@@ -0,0 +1,7 @@
+# 1.1.0 (2017-11-1)
+
+A slight breaking change. The dump-method of the `Dumper` interface has changed from `Dump` to `LitterDump` to mitigate potential collisions.
+
+# 1.0.0 (2017-10-29)
+
+Tagged 1.0.0.
diff --git a/vendor/github.com/sanity-io/litter/LICENSE b/vendor/github.com/sanity-io/litter/LICENSE
new file mode 100644
index 000000000..6a27c654f
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Sanity.io.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/sanity-io/litter/README.md b/vendor/github.com/sanity-io/litter/README.md
new file mode 100644
index 000000000..3dd83bc7f
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/README.md
@@ -0,0 +1,202 @@
+[![!Build Status](https://travis-ci.org/sanity-io/litter.svg?branch=master)](https://travis-ci.org/sanity-io/litter)
+
+# Litter
+
+**Litter is a pretty printer library for Go data structures to aid in debugging and testing.**
+
+---
+
+Litter is provided by
+
+<a href="https://www.sanity.io/?utm_source=GitHub&utm_campaign=litter" rel="nofollow" target="_blank">
+ <img src="https://www.sanity.io/static/images/logo_red.svg?v=2" width="300"><br />
+ Sanity: The Headless CMS Construction Kit
+</a>
+
+---
+
+Litter named for the fact that it outputs *literals*, which you *litter* your output with. As a side benefit, all Litter output is syntactically correct Go. You can use Litter to emit data during debug, and it's also really nice for "snapshot data" in unit tests, since it produces consistent, sorted output. Litter was inspired by [Spew](https://github.com/davecgh/go-spew), but focuses on terseness and readability.
+
+### Basic example
+
+This:
+
+```go
+type Person struct {
+ Name string
+ Age int
+ Parent *Person
+}
+
+litter.Dump(Person{
+ Name: "Bob",
+ Age: 20,
+ Parent: &Person{
+ Name: "Jane",
+ Age: 50,
+ },
+})
+```
+
+will output:
+
+```
+Person{
+ Name: "Bob",
+ Age: 20,
+ Parent: &Person{
+ Name: "Jane",
+ Age: 50,
+ },
+}
+```
+
+### Use in tests
+
+Litter is a great alternative to JSON or YAML for providing "snapshots" or example data. For example:
+
+```go
+func TestSearch(t *testing.T) {
+ result := DoSearch()
+
+ actual := litterOpts.Sdump(result)
+ expected, err := ioutil.ReadFile("testdata.txt")
+ if err != nil {
+ // First run, write test data since it doesn't exist
+ if !os.IsNotExist(err) {
+ t.Error(err)
+ }
+ ioutil.Write("testdata.txt", actual, 0644)
+ actual = expected
+ }
+ if expected != actual {
+ t.Errorf("Expected %s, got %s", expected, actual)
+ }
+}
+```
+
+The first run will use Litter to write the data to `testdata.txt`. On subsequent runs, the test will compare the data. Since Litter always provides a consistent view of a value, you can compare the strings directly.
+
+### Circular references
+
+Litter detects circular references or aliasing, and will replace additional references to the same object with aliases. For example:
+
+```go
+type Circular struct {
+ Self *Circular
+}
+
+selfref := Circular{}
+selfref.Self = &selfref
+
+litter.Dump(selfref)
+```
+
+will output:
+
+```
+Circular { // p0
+ Self: p0,
+}
+```
+
+## Installation
+
+```bash
+$ go get -u github.com/sanity-io/litter
+```
+
+## Quick start
+
+Add this import line to the file you're working in:
+
+```go
+import "github.com/sanity-io/litter"
+```
+
+To dump a variable with full newlines, indentation, type, and aliasing information, use `Dump` or `Sdump`:
+
+```go
+litter.Dump(myVar1)
+str := litter.Sdump(myVar1)
+```
+
+### `litter.Dump(value, ...)`
+
+Dumps the data structure to STDOUT.
+
+### `litter.Sdump(value, ...)`
+
+Returns the dump as a string
+
+## Configuration
+
+You can configure litter globally by modifying the default `litter.Config`
+
+```go
+// Strip all package names from types
+litter.Config.StripPackageNames = true
+
+// Hide private struct fields from dumped structs
+litter.Config.HidePrivateFields = true
+
+// Hide fields matched with given regexp if it is not nil. It is set up to hide fields generate with protoc-gen-go
+litter.Config.FieldExclusions = regexp.MustCompile(`^(XXX_.*)$`)
+
+// Sets a "home" package. The package name will be stripped from all its types
+litter.Config.HomePackage = "mypackage"
+
+// Sets separator used when multiple arguments are passed to Dump() or Sdump().
+litter.Config.Separator = "\n"
+
+// Use compact output: strip newlines and other unnecessary whitespace
+litter.Config.Compact = true
+
+// Prevents duplicate pointers from being replaced by placeholder variable names (except in necessary, in the case
+// of circular references)
+litter.Config.DisablePointerReplacement = true
+```
+
+### `litter.Options`
+
+Allows you to configure a local configuration of litter to allow for proper compartmentalization of state at the expense of some comfort:
+
+``` go
+ sq := litter.Options {
+ HidePrivateFields: true,
+ HomePackage: "thispack",
+ Separator: " ",
+ }
+
+ sq.Dump("dumped", "with", "local", "settings")
+```
+
+## Custom dumpers
+
+Implement the interface Dumper on your types to take control of how your type is dumped.
+
+``` go
+type Dumper interface {
+ LitterDump(w io.Writer)
+}
+```
+
+Just write your custom dump to the provided stream, using multiple lines divided by `"\n"` if you need. Litter
+might indent your output according to context, and optionally decorate your first line with a pointer comment
+where appropriate.
+
+A couple of examples from the test suite:
+
+``` go
+type CustomMultiLineDumper struct {}
+
+func (cmld *CustomMultiLineDumper) LitterDump(w io.Writer) {
+ w.Write([]byte("{\n multi\n line\n}"))
+}
+
+type CustomSingleLineDumper int
+
+func (csld CustomSingleLineDumper) LitterDump(w io.Writer) {
+ w.Write([]byte("<custom>"))
+}
+````
diff --git a/vendor/github.com/sanity-io/litter/dump.go b/vendor/github.com/sanity-io/litter/dump.go
new file mode 100644
index 000000000..3dfbb9dbd
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/dump.go
@@ -0,0 +1,528 @@
+package litter
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var (
+ packageNameStripperRegexp = regexp.MustCompile(`\b[a-zA-Z_]+[a-zA-Z_0-9]+\.`)
+ compactTypeRegexp = regexp.MustCompile(`\s*([,;{}()])\s*`)
+)
+
+// Dumper is the interface for implementing custom dumper for your types.
+type Dumper interface {
+ LitterDump(w io.Writer)
+}
+
+// Options represents configuration options for litter
+type Options struct {
+ Compact bool
+ StripPackageNames bool
+ HidePrivateFields bool
+ HideZeroValues bool
+ FieldExclusions *regexp.Regexp
+ FieldFilter func(reflect.StructField, reflect.Value) bool
+ HomePackage string
+ Separator string
+ StrictGo bool
+ DumpFunc func(reflect.Value, io.Writer) bool
+
+ // DisablePointerReplacement, if true, disables the replacing of pointer data with variable names
+ // when it's safe. This is useful for diffing two structures, where pointer variables would cause
+ // false changes. However, circular graphs are still detected and elided to avoid infinite output.
+ DisablePointerReplacement bool
+}
+
+// Config is the default config used when calling Dump
+var Config = Options{
+ StripPackageNames: false,
+ HidePrivateFields: true,
+ FieldExclusions: regexp.MustCompile(`^(XXX_.*)$`), // XXX_ is a prefix of fields generated by protoc-gen-go
+ Separator: " ",
+}
+
+type dumpState struct {
+ w io.Writer
+ depth int
+ config *Options
+ pointers ptrmap
+ visitedPointers ptrmap
+ parentPointers ptrmap
+ currentPointer *ptrinfo
+ homePackageRegexp *regexp.Regexp
+}
+
+func (s *dumpState) write(b []byte) {
+ if _, err := s.w.Write(b); err != nil {
+ panic(err)
+ }
+}
+
+func (s *dumpState) writeString(str string) {
+ s.write([]byte(str))
+}
+
+func (s *dumpState) indent() {
+ if !s.config.Compact {
+ s.write(bytes.Repeat([]byte(" "), s.depth))
+ }
+}
+
+func (s *dumpState) newlineWithPointerNameComment() {
+ if ptr := s.currentPointer; ptr != nil {
+ if s.config.Compact {
+ s.write([]byte(fmt.Sprintf("/*%s*/", ptr.label())))
+ } else {
+ s.write([]byte(fmt.Sprintf(" // %s\n", ptr.label())))
+ }
+ s.currentPointer = nil
+ return
+ }
+ if !s.config.Compact {
+ s.write([]byte("\n"))
+ }
+}
+
+func (s *dumpState) dumpType(v reflect.Value) {
+ typeName := v.Type().String()
+ if s.config.StripPackageNames {
+ typeName = packageNameStripperRegexp.ReplaceAllLiteralString(typeName, "")
+ } else if s.homePackageRegexp != nil {
+ typeName = s.homePackageRegexp.ReplaceAllLiteralString(typeName, "")
+ }
+ if s.config.Compact {
+ typeName = compactTypeRegexp.ReplaceAllString(typeName, "$1")
+ }
+ s.write([]byte(typeName))
+}
+
+func (s *dumpState) dumpSlice(v reflect.Value) {
+ s.dumpType(v)
+ numEntries := v.Len()
+ if numEntries == 0 {
+ s.write([]byte("{}"))
+ return
+ }
+ s.write([]byte("{"))
+ s.newlineWithPointerNameComment()
+ s.depth++
+ for i := 0; i < numEntries; i++ {
+ s.indent()
+ s.dumpVal(v.Index(i))
+ if !s.config.Compact || i < numEntries-1 {
+ s.write([]byte(","))
+ }
+ s.newlineWithPointerNameComment()
+ }
+ s.depth--
+ s.indent()
+ s.write([]byte("}"))
+}
+
+func (s *dumpState) dumpStruct(v reflect.Value) {
+ dumpPreamble := func() {
+ s.dumpType(v)
+ s.write([]byte("{"))
+ s.newlineWithPointerNameComment()
+ s.depth++
+ }
+ preambleDumped := false
+ vt := v.Type()
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ vtf := vt.Field(i)
+ if s.config.HidePrivateFields && vtf.PkgPath != "" || s.config.FieldExclusions != nil && s.config.FieldExclusions.MatchString(vtf.Name) {
+ continue
+ }
+ if s.config.FieldFilter != nil && !s.config.FieldFilter(vtf, v.Field(i)) {
+ continue
+ }
+ if s.config.HideZeroValues && isZeroValue(v.Field(i)) {
+ continue
+ }
+ if !preambleDumped {
+ dumpPreamble()
+ preambleDumped = true
+ }
+ s.indent()
+ s.write([]byte(vtf.Name))
+ if s.config.Compact {
+ s.write([]byte(":"))
+ } else {
+ s.write([]byte(": "))
+ }
+ s.dumpVal(v.Field(i))
+ if !s.config.Compact || i < numFields-1 {
+ s.write([]byte(","))
+ }
+ s.newlineWithPointerNameComment()
+ }
+ if preambleDumped {
+ s.depth--
+ s.indent()
+ s.write([]byte("}"))
+ } else {
+ // There were no fields dumped
+ s.dumpType(v)
+ s.write([]byte("{}"))
+ }
+}
+
+func (s *dumpState) dumpMap(v reflect.Value) {
+ if v.IsNil() {
+ s.dumpType(v)
+ s.writeString("(nil)")
+ return
+ }
+
+ s.dumpType(v)
+
+ keys := v.MapKeys()
+ if len(keys) == 0 {
+ s.write([]byte("{}"))
+ return
+ }
+
+ s.write([]byte("{"))
+ s.newlineWithPointerNameComment()
+ s.depth++
+ sort.Sort(mapKeySorter{
+ keys: keys,
+ options: s.config,
+ })
+ numKeys := len(keys)
+ for i, key := range keys {
+ s.indent()
+ s.dumpVal(key)
+ if s.config.Compact {
+ s.write([]byte(":"))
+ } else {
+ s.write([]byte(": "))
+ }
+ s.dumpVal(v.MapIndex(key))
+ if !s.config.Compact || i < numKeys-1 {
+ s.write([]byte(","))
+ }
+ s.newlineWithPointerNameComment()
+ }
+ s.depth--
+ s.indent()
+ s.write([]byte("}"))
+}
+
+func (s *dumpState) dumpFunc(v reflect.Value) {
+ parts := strings.Split(runtime.FuncForPC(v.Pointer()).Name(), "/")
+ name := parts[len(parts)-1]
+
+ // Anonymous function
+ if strings.Count(name, ".") > 1 {
+ s.dumpType(v)
+ } else {
+ if s.config.StripPackageNames {
+ name = packageNameStripperRegexp.ReplaceAllLiteralString(name, "")
+ } else if s.homePackageRegexp != nil {
+ name = s.homePackageRegexp.ReplaceAllLiteralString(name, "")
+ }
+ if s.config.Compact {
+ name = compactTypeRegexp.ReplaceAllString(name, "$1")
+ }
+ s.write([]byte(name))
+ }
+}
+
+func (s *dumpState) dumpChan(v reflect.Value) {
+ vType := v.Type()
+ res := []byte(vType.String())
+ s.write(res)
+}
+
+func (s *dumpState) dumpCustom(v reflect.Value, buf *bytes.Buffer) {
+
+ // Dump the type
+ s.dumpType(v)
+
+ if s.config.Compact {
+ s.write(buf.Bytes())
+ return
+ }
+
+ // Now output the dump taking care to apply the current indentation-level
+ // and pointer name comments.
+ var err error
+ firstLine := true
+ for err == nil {
+ var lineBytes []byte
+ lineBytes, err = buf.ReadBytes('\n')
+ line := strings.TrimRight(string(lineBytes), " \n")
+
+ if err != nil && err != io.EOF {
+ break
+ }
+ // Do not indent first line
+ if firstLine {
+ firstLine = false
+ } else {
+ s.indent()
+ }
+ s.write([]byte(line))
+
+ // At EOF we're done
+ if err == io.EOF {
+ return
+ }
+ s.newlineWithPointerNameComment()
+ }
+ panic(err)
+}
+
+func (s *dumpState) dump(value interface{}) {
+ if value == nil {
+ printNil(s.w)
+ return
+ }
+ v := reflect.ValueOf(value)
+ s.dumpVal(v)
+}
+
+func (s *dumpState) descendIntoPossiblePointer(value reflect.Value, f func()) {
+ canonicalize := true
+ if isPointerValue(value) {
+ // If elision disabled, and this is not a circular reference, don't canonicalize
+ if s.config.DisablePointerReplacement && s.parentPointers.add(value) {
+ canonicalize = false
+ }
+
+ // Add to stack of pointers we're recursively descending into
+ s.parentPointers.add(value)
+ defer s.parentPointers.remove(value)
+ }
+
+ if !canonicalize {
+ ptr, _ := s.pointerFor(value)
+ s.currentPointer = ptr
+ f()
+ return
+ }
+
+ ptr, firstVisit := s.pointerFor(value)
+ if ptr == nil {
+ f()
+ return
+ }
+ if firstVisit {
+ s.currentPointer = ptr
+ f()
+ return
+ }
+ s.write([]byte(ptr.label()))
+}
+
+func (s *dumpState) dumpVal(value reflect.Value) {
+ if value.Kind() == reflect.Ptr && value.IsNil() {
+ s.write([]byte("nil"))
+ return
+ }
+
+ v := deInterface(value)
+ kind := v.Kind()
+
+ // Try to handle with dump func
+ if s.config.DumpFunc != nil {
+ buf := new(bytes.Buffer)
+ if s.config.DumpFunc(v, buf) {
+ s.dumpCustom(v, buf)
+ return
+ }
+ }
+
+ // Handle custom dumpers
+ dumperType := reflect.TypeOf((*Dumper)(nil)).Elem()
+ if v.Type().Implements(dumperType) {
+ s.descendIntoPossiblePointer(v, func() {
+ // Run the custom dumper buffering the output
+ buf := new(bytes.Buffer)
+ dumpFunc := v.MethodByName("LitterDump")
+ dumpFunc.Call([]reflect.Value{reflect.ValueOf(buf)})
+ s.dumpCustom(v, buf)
+ })
+ return
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+ s.write([]byte("<invalid>"))
+
+ case reflect.Bool:
+ printBool(s.w, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(s.w, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(s.w, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(s.w, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(s.w, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(s.w, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(s.w, v.Complex(), 64)
+
+ case reflect.String:
+ s.write([]byte(strconv.Quote(v.String())))
+
+ case reflect.Slice:
+ if v.IsNil() {
+ printNil(s.w)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ s.descendIntoPossiblePointer(v, func() {
+ s.dumpSlice(v)
+ })
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ printNil(s.w)
+ }
+
+ case reflect.Ptr:
+ s.descendIntoPossiblePointer(v, func() {
+ if s.config.StrictGo {
+ s.writeString(fmt.Sprintf("(func(v %s) *%s { return &v })(", v.Elem().Type(), v.Elem().Type()))
+ s.dumpVal(v.Elem())
+ s.writeString(")")
+ } else {
+ s.writeString("&")
+ s.dumpVal(v.Elem())
+ }
+ })
+
+ case reflect.Map:
+ s.descendIntoPossiblePointer(v, func() {
+ s.dumpMap(v)
+ })
+
+ case reflect.Struct:
+ s.dumpStruct(v)
+
+ case reflect.Func:
+ s.dumpFunc(v)
+
+ case reflect.Chan:
+ s.dumpChan(v)
+
+ default:
+ if v.CanInterface() {
+ s.writeString(fmt.Sprintf("%v", v.Interface()))
+ } else {
+ s.writeString(fmt.Sprintf("%v", v.String()))
+ }
+ }
+}
+
+// registers that the value has been visited and checks to see if it is one of the
+// pointers we will see multiple times. If it is, it returns a temporary name for this
+// pointer. It also returns a boolean value indicating whether this is the first time
+// this name is returned so the caller can decide whether the contents of the pointer
+// has been dumped before or not.
+func (s *dumpState) pointerFor(v reflect.Value) (*ptrinfo, bool) {
+ if isPointerValue(v) {
+ if info, ok := s.pointers.get(v); ok {
+ firstVisit := s.visitedPointers.add(v)
+ return info, firstVisit
+ }
+ }
+ return nil, false
+}
+
+// prepares a new state object for dumping the provided value
+func newDumpState(value interface{}, options *Options, writer io.Writer) *dumpState {
+ result := &dumpState{
+ config: options,
+ pointers: mapReusedPointers(reflect.ValueOf(value)),
+ w: writer,
+ }
+
+ if options.HomePackage != "" {
+ result.homePackageRegexp = regexp.MustCompile(fmt.Sprintf("\\b%s\\.", options.HomePackage))
+ }
+
+ return result
+}
+
+// Dump a value to stdout
+func Dump(value ...interface{}) {
+ (&Config).Dump(value...)
+}
+
+// Sdump dumps a value to a string
+func Sdump(value ...interface{}) string {
+ return (&Config).Sdump(value...)
+}
+
+// Dump a value to stdout according to the options
+func (o Options) Dump(values ...interface{}) {
+ for i, value := range values {
+ state := newDumpState(value, &o, os.Stdout)
+ if i > 0 {
+ state.write([]byte(o.Separator))
+ }
+ state.dump(value)
+ }
+ _, _ = os.Stdout.Write([]byte("\n"))
+}
+
+// Sdump dumps a value to a string according to the options
+func (o Options) Sdump(values ...interface{}) string {
+ buf := new(bytes.Buffer)
+ for i, value := range values {
+ if i > 0 {
+ _, _ = buf.Write([]byte(o.Separator))
+ }
+ state := newDumpState(value, &o, buf)
+ state.dump(value)
+ }
+ return buf.String()
+}
+
+type mapKeySorter struct {
+ keys []reflect.Value
+ options *Options
+}
+
+func (s mapKeySorter) Len() int {
+ return len(s.keys)
+}
+
+func (s mapKeySorter) Swap(i, j int) {
+ s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
+}
+
+func (s mapKeySorter) Less(i, j int) bool {
+ ibuf := new(bytes.Buffer)
+ jbuf := new(bytes.Buffer)
+ newDumpState(s.keys[i], s.options, ibuf).dumpVal(s.keys[i])
+ newDumpState(s.keys[j], s.options, jbuf).dumpVal(s.keys[j])
+ return ibuf.String() < jbuf.String()
+}
diff --git a/vendor/github.com/sanity-io/litter/go.mod b/vendor/github.com/sanity-io/litter/go.mod
new file mode 100644
index 000000000..c1c20c939
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/go.mod
@@ -0,0 +1,9 @@
+module github.com/sanity-io/litter
+
+go 1.14
+
+require (
+ github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b // indirect
+ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 // indirect
+ github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312
+)
diff --git a/vendor/github.com/sanity-io/litter/go.sum b/vendor/github.com/sanity-io/litter/go.sum
new file mode 100644
index 000000000..800ae0053
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/go.sum
@@ -0,0 +1,6 @@
+github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b h1:XxMZvQZtTXpWMNWK82vdjCLCe7uGMFXdTsJH0v3Hkvw=
+github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 h1:GD+A8+e+wFkqje55/2fOVnZPkoDIu1VooBWfNrnY8Uo=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312 h1:UsFdQ3ZmlzS0BqZYGxvYaXvFGUbCmPGy8DM7qWJJiIQ=
+github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
diff --git a/vendor/github.com/sanity-io/litter/pointers.go b/vendor/github.com/sanity-io/litter/pointers.go
new file mode 100644
index 000000000..2c57d79ae
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/pointers.go
@@ -0,0 +1,167 @@
+package litter
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+)
+
+// mapReusedPointers takes a structure, and recursively maps all pointers mentioned in the tree,
+// detecting circular references, and providing a list of all pointers that was referenced at
+// least twice by the provided structure.
+func mapReusedPointers(v reflect.Value) ptrmap {
+ pm := &pointerVisitor{}
+ pm.consider(v)
+ return pm.reused
+}
+
+// A map of pointers.
+type ptrinfo struct {
+ id int
+ parent *ptrmap
+}
+
+func (p *ptrinfo) label() string {
+ if p.id == -1 {
+ p.id = p.parent.count
+ p.parent.count++
+ }
+ return fmt.Sprintf("p%d", p.id)
+}
+
+type ptrkey struct {
+ p uintptr
+ t reflect.Type
+}
+
+func ptrkeyFor(v reflect.Value) (k ptrkey) {
+ k.p = v.Pointer()
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ if v.IsValid() {
+ k.t = v.Type()
+ }
+ return
+}
+
+type ptrmap struct {
+ m map[ptrkey]*ptrinfo
+ count int
+}
+
+// Returns true if contains a pointer.
+func (pm *ptrmap) contains(v reflect.Value) bool {
+ if pm.m != nil {
+ _, ok := pm.m[ptrkeyFor(v)]
+ return ok
+ }
+ return false
+}
+
+// Gets a pointer.
+func (pm *ptrmap) get(v reflect.Value) (*ptrinfo, bool) {
+ if pm.m != nil {
+ p, ok := pm.m[ptrkeyFor(v)]
+ return p, ok
+ }
+ return nil, false
+}
+
+// Removes a pointer.
+func (pm *ptrmap) remove(v reflect.Value) {
+ if pm.m != nil {
+ delete(pm.m, ptrkeyFor(v))
+ }
+}
+
+// Adds a pointer.
+func (pm *ptrmap) add(p reflect.Value) bool {
+ if pm.contains(p) {
+ return false
+ }
+ pm.put(p)
+ return true
+}
+
+// Adds a pointer (slow path).
+func (pm *ptrmap) put(v reflect.Value) {
+ if pm.m == nil {
+ pm.m = make(map[ptrkey]*ptrinfo, 31)
+ }
+
+ key := ptrkeyFor(v)
+ if _, ok := pm.m[key]; !ok {
+ pm.m[key] = &ptrinfo{id: -1, parent: pm}
+ }
+}
+
+type pointerVisitor struct {
+ pointers ptrmap
+ reused ptrmap
+}
+
+// Recursively consider v and each of its children, updating the map according to the
+// semantics of MapReusedPointers
+func (pv *pointerVisitor) consider(v reflect.Value) {
+ if v.Kind() == reflect.Invalid {
+ return
+ }
+ if isPointerValue(v) { // pointer is 0 for unexported fields
+ if pv.tryAddPointer(v) {
+ // No use descending inside this value, since it have been seen before and all its descendants
+ // have been considered
+ return
+ }
+ }
+
+ // Now descend into any children of this value
+ switch v.Kind() {
+ case reflect.Slice, reflect.Array:
+ numEntries := v.Len()
+ for i := 0; i < numEntries; i++ {
+ pv.consider(v.Index(i))
+ }
+
+ case reflect.Interface:
+ pv.consider(v.Elem())
+
+ case reflect.Ptr:
+ pv.consider(v.Elem())
+
+ case reflect.Map:
+ keys := v.MapKeys()
+ sort.Sort(mapKeySorter{
+ keys: keys,
+ options: &Config,
+ })
+ for _, key := range keys {
+ pv.consider(v.MapIndex(key))
+ }
+
+ case reflect.Struct:
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ pv.consider(v.Field(i))
+ }
+ }
+}
+
+// addPointer to the pointerMap, update reusedPointers. Returns true if pointer was reused
+func (pv *pointerVisitor) tryAddPointer(v reflect.Value) bool {
+ // Is this allready known to be reused?
+ if pv.reused.contains(v) {
+ return true
+ }
+
+ // Have we seen it once before?
+ if pv.pointers.contains(v) {
+ // Add it to the register of pointers we have seen more than once
+ pv.reused.add(v)
+ return true
+ }
+
+ // This pointer was new to us
+ pv.pointers.add(v)
+ return false
+}
diff --git a/vendor/github.com/sanity-io/litter/print.go b/vendor/github.com/sanity-io/litter/print.go
new file mode 100644
index 000000000..88adf5b41
--- /dev/null
+++ b/vendor/github.com/sanity-io/litter/print.go
@@ -0,0 +1,50 @@
+package litter
<