pax_global_header00006660000000000000000000000064145646054610014525gustar00rootroot0000000000000052 comment=3d05a4813c4bc97890471226ba1cf7f240a376ac repr-0.4.0/000077500000000000000000000000001456460546100124765ustar00rootroot00000000000000repr-0.4.0/.github/000077500000000000000000000000001456460546100140365ustar00rootroot00000000000000repr-0.4.0/.github/FUNDING.yml000066400000000000000000000000251456460546100156500ustar00rootroot00000000000000github: [alecthomas] repr-0.4.0/.github/workflows/000077500000000000000000000000001456460546100160735ustar00rootroot00000000000000repr-0.4.0/.github/workflows/ci.yml000066400000000000000000000010731456460546100172120ustar00rootroot00000000000000on: push: branches: - master pull_request: name: CI jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Init Hermit run: ./bin/hermit env -r >> $GITHUB_ENV - name: Test run: go test ./... lint: name: Lint runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Init Hermit run: ./bin/hermit env -r >> $GITHUB_ENV - name: golangci-lint run: golangci-lint run repr-0.4.0/COPYING000066400000000000000000000020661456460546100135350ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Alec Thomas 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. repr-0.4.0/README.md000066400000000000000000000041451456460546100137610ustar00rootroot00000000000000# Python's repr() for Go [![](https://godoc.org/github.com/alecthomas/repr?status.svg)](http://godoc.org/github.com/alecthomas/repr) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/repr.svg)](https://circleci.com/gh/alecthomas/repr) This package attempts to represent Go values in a form that can be used almost directly in Go source code. Unfortunately, some values (such as pointers to basic types) can not be represented directly in Go. These values will be represented as `&`. eg. `&23` ## Example ```go type test struct { S string I int A []int } func main() { repr.Print(&test{ S: "String", I: 123, A: []int{1, 2, 3}, }) } ``` Outputs ``` &main.test{S: "String", I: 123, A: []int{1, 2, 3}} ``` ## Why repr and not [pp](https://github.com/k0kubun/pp)? pp is designed for printing coloured output to consoles, with (seemingly?) no way to disable this. If you don't want coloured output (eg. for use in diffs, logs, etc.) repr is for you. ## Why repr and not [go-spew](https://github.com/davecgh/go-spew)? Repr deliberately contains much less metadata about values. It is designed to (generally) be copyable directly into source code. Compare go-spew: ```go (parser.expression) (len=1 cap=1) { (parser.alternative) (len=1 cap=1) { ([]interface {}) (len=1 cap=1) { (*parser.repitition)(0xc82000b220)({ expression: (parser.expression) (len=2 cap=2) { (parser.alternative) (len=1 cap=1) { ([]interface {}) (len=1 cap=1) { (parser.str) (len=1) "a" } }, (parser.alternative) (len=1 cap=1) { ([]interface {}) (len=1 cap=1) { (*parser.self)(0x593ef0)({ }) } } } }) } } } ``` To repr: ```go parser.expression{ parser.alternative{ []interface {}{ &parser.repitition{ expression: parser.expression{ parser.alternative{ []interface {}{ parser.str("a"), }, }, parser.alternative{ []interface {}{ &parser.self{ }, }, }, }, }, }, }, } ``` repr-0.4.0/bin/000077500000000000000000000000001456460546100132465ustar00rootroot00000000000000repr-0.4.0/bin/.go-1.22.0.pkg000077700000000000000000000000001456460546100163602hermitustar00rootroot00000000000000repr-0.4.0/bin/.golangci-lint-1.56.2.pkg000077700000000000000000000000001456460546100205132hermitustar00rootroot00000000000000repr-0.4.0/bin/README.hermit.md000066400000000000000000000004161456460546100160150ustar00rootroot00000000000000# Hermit environment This is a [Hermit](https://github.com/cashapp/hermit) bin directory. The symlinks in this directory are managed by Hermit and will automatically download and install Hermit itself as well as packages. These packages are local to this environment. repr-0.4.0/bin/activate-hermit000077500000000000000000000010621456460546100162610ustar00rootroot00000000000000#!/bin/bash # This file must be used with "source bin/activate-hermit" from bash or zsh. # You cannot run it directly if [ "${BASH_SOURCE-}" = "$0" ]; then echo "You must source this script: \$ source $0" >&2 exit 33 fi BIN_DIR="$(dirname "${BASH_SOURCE[0]:-${(%):-%x}}")" if "${BIN_DIR}/hermit" noop > /dev/null; then eval "$("${BIN_DIR}/hermit" activate "${BIN_DIR}/..")" if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ]; then hash -r 2>/dev/null fi echo "Hermit environment $("${HERMIT_ENV}"/bin/hermit env HERMIT_ENV) activated" fi repr-0.4.0/bin/go000077700000000000000000000000001456460546100154752.go-1.22.0.pkgustar00rootroot00000000000000repr-0.4.0/bin/gofmt000077700000000000000000000000001456460546100162042.go-1.22.0.pkgustar00rootroot00000000000000repr-0.4.0/bin/golangci-lint000077700000000000000000000000001456460546100217522.golangci-lint-1.56.2.pkgustar00rootroot00000000000000repr-0.4.0/bin/hermit000077500000000000000000000013611456460546100144650ustar00rootroot00000000000000#!/bin/bash set -eo pipefail if [ -z "${HERMIT_STATE_DIR}" ]; then case "$(uname -s)" in Darwin) export HERMIT_STATE_DIR="${HOME}/Library/Caches/hermit" ;; Linux) export HERMIT_STATE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/hermit" ;; esac fi export HERMIT_DIST_URL="${HERMIT_DIST_URL:-https://github.com/cashapp/hermit/releases/download/stable}" HERMIT_CHANNEL="$(basename "${HERMIT_DIST_URL}")" export HERMIT_CHANNEL export HERMIT_EXE=${HERMIT_EXE:-${HERMIT_STATE_DIR}/pkg/hermit@${HERMIT_CHANNEL}/hermit} if [ ! -x "${HERMIT_EXE}" ]; then echo "Bootstrapping ${HERMIT_EXE} from ${HERMIT_DIST_URL}" 1>&2 curl -fsSL "${HERMIT_DIST_URL}/install.sh" | /bin/bash 1>&2 fi exec "${HERMIT_EXE}" --level=fatal exec "$0" -- "$@" repr-0.4.0/bin/hermit.hcl000066400000000000000000000000001456460546100152140ustar00rootroot00000000000000repr-0.4.0/go.mod000066400000000000000000000000531456460546100136020ustar00rootroot00000000000000module github.com/alecthomas/repr go 1.18 repr-0.4.0/go.sum000066400000000000000000000000001456460546100136170ustar00rootroot00000000000000repr-0.4.0/renovate.json5000066400000000000000000000004311456460546100152770ustar00rootroot00000000000000{ $schema: "https://docs.renovatebot.com/renovate-schema.json", extends: [ "config:recommended", ":semanticCommits", ":semanticCommitTypeAll(chore)", ":semanticCommitScope(deps)", "group:allNonMajor", "schedule:earlyMondays", // Run once a week. ], } repr-0.4.0/repr.go000066400000000000000000000270161456460546100140030ustar00rootroot00000000000000// Package repr attempts to represent Go values in a form that can be copy-and-pasted into source // code directly. // // Some values (such as pointers to basic types) can not be represented directly in // Go. These values will be output as `&`. eg. `&23` package repr import ( "bytes" "fmt" "io" "os" "reflect" "sort" "strings" "time" "unsafe" ) var ( // "Real" names of basic kinds, used to differentiate type aliases. realKindName = map[reflect.Kind]string{ reflect.Bool: "bool", reflect.Int: "int", reflect.Int8: "int8", reflect.Int16: "int16", reflect.Int32: "int32", reflect.Int64: "int64", reflect.Uint: "uint", reflect.Uint8: "uint8", reflect.Uint16: "uint16", reflect.Uint32: "uint32", reflect.Uint64: "uint64", reflect.Uintptr: "uintptr", reflect.Float32: "float32", reflect.Float64: "float64", reflect.Complex64: "complex64", reflect.Complex128: "complex128", reflect.Array: "array", reflect.Chan: "chan", reflect.Func: "func", reflect.Map: "map", reflect.Slice: "slice", reflect.String: "string", } goStringerType = reflect.TypeOf((*fmt.GoStringer)(nil)).Elem() anyType = reflect.TypeOf((*any)(nil)).Elem() byteSliceType = reflect.TypeOf([]byte{}) ) // Default prints to os.Stdout with two space indentation. var Default = New(os.Stdout, Indent(" ")) // An Option modifies the default behaviour of a Printer. type Option func(o *Printer) // Indent output by this much. func Indent(indent string) Option { return func(o *Printer) { o.indent = indent } } // NoIndent disables indenting. func NoIndent() Option { return Indent("") } // OmitEmpty sets whether empty field members should be omitted from output. func OmitEmpty(omitEmpty bool) Option { return func(o *Printer) { o.omitEmpty = omitEmpty } } // ExplicitTypes adds explicit typing to slice and map struct values that would normally be inferred by Go. func ExplicitTypes(ok bool) Option { return func(o *Printer) { o.explicitTypes = true } } // IgnoreGoStringer disables use of the .GoString() method. func IgnoreGoStringer() Option { return func(o *Printer) { o.ignoreGoStringer = true } } // IgnorePrivate disables private field members from output. func IgnorePrivate() Option { return func(o *Printer) { o.ignorePrivate = true } } // ScalarLiterals forces the use of literals for scalars, rather than a string representation if available. // // For example, `time.Hour` will be printed as `time.Duration(3600000000000)` rather than `time.Duration(1h0m0s)`. func ScalarLiterals() Option { return func(o *Printer) { o.useLiterals = true } } // Hide excludes fields of the given type from representation. func Hide[T any]() Option { return func(o *Printer) { t := (*T)(nil) // A bit of skulduggery so we can Hide() interfaces. rt := reflect.TypeOf(t).Elem() o.exclude[rt] = true } } // AlwaysIncludeType always includes explicit type information for each item. func AlwaysIncludeType() Option { return func(o *Printer) { o.alwaysIncludeType = true } } // Printer represents structs in a printable manner. type Printer struct { indent string omitEmpty bool ignoreGoStringer bool ignorePrivate bool alwaysIncludeType bool explicitTypes bool exclude map[reflect.Type]bool w io.Writer useLiterals bool } // New creates a new Printer on w with the given Options. func New(w io.Writer, options ...Option) *Printer { p := &Printer{ w: w, indent: " ", omitEmpty: true, exclude: map[reflect.Type]bool{}, } for _, option := range options { option(p) } return p } func (p *Printer) nextIndent(indent string) string { if p.indent != "" { return indent + p.indent } return "" } func (p *Printer) thisIndent(indent string) string { if p.indent != "" { return indent } return "" } // Print the values. func (p *Printer) Print(vs ...any) { for i, v := range vs { if i > 0 { fmt.Fprint(p.w, " ") } p.reprValue(map[reflect.Value]bool{}, reflect.ValueOf(v), "", true, false) } } // Println prints each value on a new line. func (p *Printer) Println(vs ...any) { for i, v := range vs { if i > 0 { fmt.Fprint(p.w, " ") } p.reprValue(map[reflect.Value]bool{}, reflect.ValueOf(v), "", true, false) } fmt.Fprintln(p.w) } // showType is true if struct types should be shown. isAnyValue is true if the containing value is an "any" type. func (p *Printer) reprValue(seen map[reflect.Value]bool, v reflect.Value, indent string, showStructType bool, isAnyValue bool) { // nolint: gocyclo if seen[v] { fmt.Fprint(p.w, "...") return } seen[v] = true defer delete(seen, v) if v.Kind() == reflect.Invalid || (v.Kind() == reflect.Ptr || v.Kind() == reflect.Map || v.Kind() == reflect.Chan || v.Kind() == reflect.Slice || v.Kind() == reflect.Func || v.Kind() == reflect.Interface) && v.IsNil() { fmt.Fprint(p.w, "nil") return } t := v.Type() if t == byteSliceType { fmt.Fprintf(p.w, "[]byte(%q)", v.Bytes()) return } // If we can't access a private field directly with reflection, try and do so via unsafe. if !v.CanInterface() && v.CanAddr() { uv := reflect.NewAt(t, unsafe.Pointer(v.UnsafeAddr())).Elem() if uv.CanInterface() { v = uv } } // Attempt to use fmt.GoStringer interface. if !p.ignoreGoStringer && t.Implements(goStringerType) && v.CanInterface() { fmt.Fprint(p.w, v.Interface().(fmt.GoStringer).GoString()) return } in := p.thisIndent(indent) ni := p.nextIndent(indent) switch v.Kind() { case reflect.Slice, reflect.Array: fmt.Fprintf(p.w, "%s{", substAny(v.Type())) if v.Len() == 0 { fmt.Fprint(p.w, "}") } else { if p.indent != "" { fmt.Fprintf(p.w, "\n") } for i := 0; i < v.Len(); i++ { e := v.Index(i) fmt.Fprintf(p.w, "%s", ni) p.reprValue(seen, e, ni, p.alwaysIncludeType || p.explicitTypes, v.Type().Elem() == anyType) if p.indent != "" { fmt.Fprintf(p.w, ",\n") } else if i < v.Len()-1 { fmt.Fprintf(p.w, ", ") } } fmt.Fprintf(p.w, "%s}", in) } case reflect.Chan: fmt.Fprintf(p.w, "make(") fmt.Fprintf(p.w, "%s", substAny(v.Type())) fmt.Fprintf(p.w, ", %d)", v.Cap()) case reflect.Map: fmt.Fprintf(p.w, "%s{", substAny(v.Type())) if p.indent != "" && v.Len() != 0 { fmt.Fprintf(p.w, "\n") } keys := v.MapKeys() sort.Slice(keys, func(i, j int) bool { return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) }) for i, k := range keys { kv := v.MapIndex(k) fmt.Fprintf(p.w, "%s", ni) p.reprValue(seen, k, ni, p.alwaysIncludeType || p.explicitTypes, v.Type().Key() == anyType) fmt.Fprintf(p.w, ": ") p.reprValue(seen, kv, ni, true, v.Type().Elem() == anyType) if p.indent != "" { fmt.Fprintf(p.w, ",\n") } else if i < v.Len()-1 { fmt.Fprintf(p.w, ", ") } } fmt.Fprintf(p.w, "%s}", in) case reflect.Struct: if td, ok := asTime(v); ok { timeToGo(p.w, td) } else { if showStructType { fmt.Fprintf(p.w, "%s{", substAny(v.Type())) } else { fmt.Fprint(p.w, "{") } if p.indent != "" && v.NumField() != 0 { fmt.Fprintf(p.w, "\n") } previous := false for i := 0; i < v.NumField(); i++ { t := v.Type().Field(i) if p.exclude[t.Type] { continue } f := v.Field(i) ft := f.Type() // skip private fields if p.ignorePrivate && !f.CanInterface() { continue } if p.omitEmpty && (f.IsZero() || ft.Kind() == reflect.Slice && f.Len() == 0 || ft.Kind() == reflect.Map && f.Len() == 0) { continue } if previous && p.indent == "" { fmt.Fprintf(p.w, ", ") } previous = true fmt.Fprintf(p.w, "%s%s: ", ni, t.Name) p.reprValue(seen, f, ni, true, t.Type == anyType) // if private fields should be ignored, look up if a public // field need to be displayed and breaks at the first public // field found preventing from looping over all remaining // fields. // // If no other field need to be displayed, continue and do // not print a comma. // // This prevents from having a trailing comma if a private // field ends a structure. if p.ignorePrivate { nc := false for j := i + 1; j < v.NumField(); j++ { if v.Field(j).CanInterface() { nc = true // exit for j loop break } } // Skip comma display if no remaining public field found. if !nc { continue } } if p.indent != "" { fmt.Fprintf(p.w, ",\n") } } fmt.Fprintf(p.w, "%s}", indent) } case reflect.Ptr: if v.IsNil() { fmt.Fprintf(p.w, "nil") return } if showStructType { fmt.Fprintf(p.w, "&") } p.reprValue(seen, v.Elem(), indent, showStructType, false) case reflect.String: if t.Name() != "string" || p.alwaysIncludeType { fmt.Fprintf(p.w, "%s(%q)", t, v.String()) } else { fmt.Fprintf(p.w, "%q", v.String()) } case reflect.Interface: if v.IsNil() { fmt.Fprintf(p.w, "%s(nil)", substAny(v.Type())) } else { p.reprValue(seen, v.Elem(), indent, true, true) } case reflect.Func: fmt.Fprint(p.w, substAny(v.Type())) default: value := fmt.Sprintf("%v", v) if p.useLiterals { value = fmt.Sprintf("%#v", v) } if t.Name() != realKindName[t.Kind()] || p.alwaysIncludeType || isAnyValue { fmt.Fprintf(p.w, "%s(%s)", t, value) } else { fmt.Fprintf(p.w, "%s", value) } } } func asTime(v reflect.Value) (time.Time, bool) { if !v.CanInterface() { return time.Time{}, false } t, ok := v.Interface().(time.Time) return t, ok } // String returns a string representing v. func String(v any, options ...Option) string { w := bytes.NewBuffer(nil) options = append([]Option{NoIndent()}, options...) p := New(w, options...) p.Print(v) return w.String() } func extractOptions(vs ...any) (args []any, options []Option) { for _, v := range vs { if o, ok := v.(Option); ok { options = append(options, o) } else { args = append(args, v) } } return } // Println prints v to os.Stdout, one per line. func Println(vs ...any) { args, options := extractOptions(vs...) New(os.Stdout, options...).Println(args...) } // Print writes a representation of v to os.Stdout, separated by spaces. func Print(vs ...any) { args, options := extractOptions(vs...) New(os.Stdout, options...).Print(args...) } func timeToGo(w io.Writer, t time.Time) { if t.IsZero() { fmt.Fprint(w, "time.Time{}") return } var zone string switch loc := t.Location(); loc { case nil: zone = "nil" case time.UTC: zone = "time.UTC" case time.Local: zone = "time.Local" default: n, off := t.Zone() zone = fmt.Sprintf("time.FixedZone(%q, %d)", n, off) } y, m, d := t.Date() fmt.Fprintf(w, `time.Date(%d, %d, %d, %d, %d, %d, %d, %s)`, y, m, d, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), zone) } // Replace "interface {}" with "any" func substAny(t reflect.Type) string { switch t.Kind() { case reflect.Array: return fmt.Sprintf("[%d]%s", t.Len(), substAny(t.Elem())) case reflect.Slice: return "[]" + substAny(t.Elem()) case reflect.Map: return "map[" + substAny(t.Key()) + "]" + substAny(t.Elem()) case reflect.Chan: return fmt.Sprintf("%s %s", t.ChanDir(), substAny(t.Elem())) case reflect.Func: in := []string{} out := []string{} for i := 0; i < t.NumIn(); i++ { in = append(in, substAny(t.In(i))) } for i := 0; i < t.NumOut(); i++ { out = append(out, substAny(t.Out(i))) } if len(out) == 0 { return "func" + t.Name() + "(" + strings.Join(in, ", ") + ")" } return "func" + t.Name() + "(" + strings.Join(in, ", ") + ") (" + strings.Join(out, ", ") + ")" } if t == anyType { return "any" } return t.String() } repr-0.4.0/repr_test.go000066400000000000000000000145241456460546100150420ustar00rootroot00000000000000package repr import ( "bytes" "fmt" "runtime" "strings" "testing" "time" ) func equal(t *testing.T, want, have string) { t.Helper() if want != have { t.Errorf("\nWant: %q\nHave: %q", want, have) } } type anotherStruct struct { A []int } func (anotherStruct) String() string { return "anotherStruct" } type testStruct struct { S string I *int A anotherStruct } type testStructWithInterfaceField struct { S string I fmt.Stringer } func TestHide(t *testing.T) { actual := testStruct{ S: "str", A: anotherStruct{A: []int{1}}, } equal(t, `repr.testStruct{S: "str"}`, String(actual, Hide[anotherStruct]())) equal(t, "repr.testStruct{\n S: \"str\",\n}", String(actual, Indent(" "), Hide[anotherStruct]())) equal(t, "repr.testStructWithInterfaceField{S: \"str\"}", String(testStructWithInterfaceField{S: "str", I: anotherStruct{}}, Hide[fmt.Stringer]())) } func TestReprEmptyArray(t *testing.T) { equal(t, "[]string{}", String([]string{}, OmitEmpty(false))) } func TestReprEmptySliceMapFields(t *testing.T) { v := struct { S []string M map[string]string NZ []string }{[]string{}, map[string]string{}, []string{"a", "b"}} equal(t, `struct { S []string; M map[string]string; NZ []string }{NZ: []string{"a", "b"}}`, String(v, OmitEmpty(true))) } func TestReprStringArray(t *testing.T) { equal(t, "[]string{\"a\", \"b\"}", String([]string{"a", "b"})) } func TestReprIntArray(t *testing.T) { equal(t, "[]int{1, 2}", String([]int{1, 2})) } func TestReprPointerToInt(t *testing.T) { pi := new(int) *pi = 13 equal(t, `&13`, String(pi)) } func TestReprChannel(t *testing.T) { ch := make(<-chan map[string]*testStruct, 1) equal(t, `make(<-chan map[string]*repr.testStruct, 1)`, String(ch)) } func TestReprEmptyMap(t *testing.T) { equal(t, "map[string]bool{}", String(map[string]bool{})) } func TestReprMap(t *testing.T) { m := map[string]int{"b": 3, "a": 1, "c": 5} for i := 0; i < 1000; i++ { equal(t, "map[string]int{\"a\": 1, \"b\": 3, \"c\": 5}", String(m)) } } func TestReprIntMap(t *testing.T) { m := map[int]string{3: "b", 1: "a", 5: "c"} for i := 0; i < 1000; i++ { equal(t, "map[int]string{1: \"a\", 3: \"b\", 5: \"c\"}", String(m)) } } func TestReprStructWithIndent(t *testing.T) { pi := new(int) *pi = 13 s := &testStruct{ S: "String", I: pi, A: anotherStruct{ A: []int{1, 2, 3}, }, } equal(t, `&repr.testStruct{ S: "String", I: &13, A: repr.anotherStruct{ A: []int{ 1, 2, 3, }, }, }`, String(s, Indent(" "))) } func TestReprByteArray(t *testing.T) { b := []byte{1, 2, 3} equal(t, "[]byte(\"\\x01\\x02\\x03\")", String(b)) } type privateTestStruct struct { a string } func TestReprPrivateField(t *testing.T) { s := privateTestStruct{"hello"} equal(t, `repr.privateTestStruct{a: "hello"}`, String(s)) } type mixedTestStruct struct { A string b string C string _D string } func TestReprPrivateMixed(t *testing.T) { s := mixedTestStruct{"hello", "world", "goodbye", "cruel world"} equal(t, `repr.mixedTestStruct{A: "hello", b: "world", C: "goodbye", _D: "cruel world"}`, String(s)) } func TestReprPrivateMixedIgnorePrivate(t *testing.T) { s := mixedTestStruct{"hello", "world", "goodbye", "cruel world"} equal(t, `repr.mixedTestStruct{A: "hello", C: "goodbye"}`, String(s, IgnorePrivate())) } func TestReprNilAlone(t *testing.T) { var err error s := String(err) equal(t, "nil", s) } func TestExplicitTypes(t *testing.T) { arr := []*privateTestStruct{{"hello"}, nil} s := String(arr, ExplicitTypes(true)) equal(t, "[]*repr.privateTestStruct{&repr.privateTestStruct{a: \"hello\"}, nil}", s) } func TestReprNilInsideArray(t *testing.T) { arr := []*privateTestStruct{{"hello"}, nil} s := String(arr) equal(t, "[]*repr.privateTestStruct{{a: \"hello\"}, nil}", s) } func TestReprEmptySlice(t *testing.T) { a := []int{} s := String(a) equal(t, "[]int{}", s) } func TestReprNilSlice(t *testing.T) { var a []int s := String(a) equal(t, "nil", s) } type intSliceStruct struct{ f []int } func TestReprEmptySliceStruct(t *testing.T) { a := intSliceStruct{f: []int{}} s := String(a) equal(t, "repr.intSliceStruct{}", s) } func TestReprNilSliceStruct(t *testing.T) { var a intSliceStruct s := String(a) equal(t, "repr.intSliceStruct{}", s) } type Enum int func (e Enum) String() string { return "Value" } func TestEnum(t *testing.T) { v := Enum(1) s := String(v) equal(t, "repr.Enum(Value)", s) } func TestShowType(t *testing.T) { a := map[string]privateTestStruct{"foo": {"bar"}} s := String(a, AlwaysIncludeType(), Indent(" ")) equal(t, strings.TrimSpace(` map[string]repr.privateTestStruct{ string("foo"): repr.privateTestStruct{ a: string("bar"), }, } `), s) } func TestRecursiveIssue3(t *testing.T) { type data struct { parent *data children []*data } child := &data{} root := &data{children: []*data{child}} child.parent = root want := "&repr.data{children: []*repr.data{{parent: &...}}}" have := String(root) equal(t, want, have) } type MyBuffer struct { buf *bytes.Buffer } func TestReprPrivateBytes(t *testing.T) { mb := MyBuffer{ buf: bytes.NewBufferString("Hi th3re!"), } s := String(mb) switch v := runtime.Version(); { case strings.Contains(v, "go1.9"): equal(t, "repr.MyBuffer{buf: &bytes.Buffer{buf: []byte(\"Hi th3re!\"), bootstrap: [64]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}", s) case strings.Contains(v, "go1.10"), strings.Contains(v, "go1.11"): equal(t, "repr.MyBuffer{buf: &bytes.Buffer{buf: []byte(\"Hi th3re!\"), bootstrap: [64]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }}", s) default: equal(t, "repr.MyBuffer{buf: &bytes.Buffer{buf: []byte(\"Hi th3re!\")}}", s) } } func TestReprAnyNumeric(t *testing.T) { var value = []any{float64(123)} equal(t, "[]any{float64(123)}", String(value)) } func TestReprFunc(t *testing.T) { in := func(any) {} equal(t, "func(any)", String(in)) inout := func(interface{}) (any, error) { panic("not implemented") } equal(t, "func(any) (any, error)", String(inout)) } func TestScalarLiterals(t *testing.T) { d := time.Second equal(t, "time.Duration(1000000000)", String(d, ScalarLiterals())) }