Commit 6d186dff authored by Anthony Fok's avatar Anthony Fok

New upstream version 0.0~git20171013.3757840

parents
sudo: false
language: go
install: go get -t -v ./...
go: 1.9
The 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.
# Python's repr() for Go [![](https://godoc.org/github.com/alecthomas/repr?status.svg)](http://godoc.org/github.com/alecthomas/repr) [![Build Status](https://travis-ci.org/alecthomas/repr.png)](https://travis-ci.org/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 `&<value>`. 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{ },
},
},
},
},
},
},
}
```
// 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 `&<value>`. eg. `&23`
package repr
import (
"bytes"
"fmt"
"io"
"os"
"reflect"
"unsafe"
)
// "Real" names of basic kinds, used to differentiate type aliases.
var 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",
}
var goStringerType = reflect.TypeOf((*fmt.GoStringer)(nil)).Elem()
// 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 } }
func IgnoreGoStringer() Option { return func(o *Printer) { o.ignoreGoStringer = true } }
// Printer represents structs in a printable manner.
type Printer struct {
indent string
omitEmpty bool
ignoreGoStringer bool
w io.Writer
}
// 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,
}
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 ...interface{}) {
for i, v := range vs {
if i > 0 {
fmt.Fprint(p.w, " ")
}
p.reprValue(reflect.ValueOf(v), "")
}
}
// Println prints each value on a new line.
func (p *Printer) Println(vs ...interface{}) {
for i, v := range vs {
if i > 0 {
fmt.Fprint(p.w, " ")
}
p.reprValue(reflect.ValueOf(v), "")
}
fmt.Fprintln(p.w)
}
func (p *Printer) reprValue(v reflect.Value, indent string) { // nolint: gocyclo
t := v.Type()
// 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 t.Implements(goStringerType) {
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:
if p.omitEmpty && v.Len() == 0 {
return
}
fmt.Fprintf(p.w, "%s{", 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(e, ni)
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", v.Type())
fmt.Fprintf(p.w, ", %d)", v.Cap())
case reflect.Map:
fmt.Fprintf(p.w, "%s{", v.Type())
if p.indent != "" && v.Len() != 0 {
fmt.Fprintf(p.w, "\n")
}
for i, k := range v.MapKeys() {
kv := v.MapIndex(k)
fmt.Fprintf(p.w, "%s", ni)
p.reprValue(k, ni)
fmt.Fprintf(p.w, ": ")
p.reprValue(kv, ni)
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:
fmt.Fprintf(p.w, "%s{", v.Type())
if p.indent != "" && v.NumField() != 0 {
fmt.Fprintf(p.w, "\n")
}
for i := 0; i < v.NumField(); i++ {
t := v.Type().Field(i)
f := v.Field(i)
if p.omitEmpty && isZero(f) {
continue
}
fmt.Fprintf(p.w, "%s%s: ", ni, t.Name)
p.reprValue(f, ni)
if p.indent != "" {
fmt.Fprintf(p.w, ",\n")
} else if i < v.NumField()-1 {
fmt.Fprintf(p.w, ", ")
}
}
fmt.Fprintf(p.w, "%s}", indent)
case reflect.Ptr:
if v.IsNil() {
fmt.Fprintf(p.w, "nil")
return
}
fmt.Fprintf(p.w, "&")
p.reprValue(v.Elem(), indent)
case reflect.String:
if t.Name() != "string" {
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, "interface {}(nil)")
} else {
p.reprValue(v.Elem(), indent)
}
default:
if t.Name() != realKindName[t.Kind()] {
fmt.Fprintf(p.w, "%s(%v)", t, v)
} else {
fmt.Fprintf(p.w, "%v", v)
}
}
}
// String returns a string representing v.
func String(v interface{}, options ...Option) string {
w := bytes.NewBuffer(nil)
options = append([]Option{NoIndent()}, options...)
p := New(w, options...)
p.Print(v)
return w.String()
}
// Print v to os.Stdout, one per line.
func Println(v ...interface{}) {
New(os.Stdout).Println(v...)
}
// Print writes a representation of v to os.Stdout, separated by spaces.
func Print(v ...interface{}) {
New(os.Stdout).Print(v...)
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return v.IsNil()
}
return false
}
package repr
import (
"testing"
"github.com/stretchr/testify/assert"
)
type anotherStruct struct {
A []int
}
type testStruct struct {
S string
I *int
A anotherStruct
}
func TestReprEmptyArray(t *testing.T) {
assert.Equal(t, "[]string{}", String([]string{}, OmitEmpty(false)))
}
func TestReprStringArray(t *testing.T) {
assert.Equal(t, "[]string{\"a\", \"b\"}", String([]string{"a", "b"}))
}
func TestReprIntArray(t *testing.T) {
assert.Equal(t, "[]int{1, 2}", String([]int{1, 2}))
}
func TestReprPointerToInt(t *testing.T) {
pi := new(int)
*pi = 13
assert.Equal(t, `&13`, String(pi))
}
func TestReprChannel(t *testing.T) {
ch := make(<-chan map[string]*testStruct, 1)
assert.Equal(t, `make(<-chan map[string]*repr.testStruct, 1)`, String(ch))
}
func TestReprEmptyMap(t *testing.T) {
assert.Equal(t, "map[string]bool{}", String(map[string]bool{}))
}
func TestReprMap(t *testing.T) {
m := map[string]int{"a": 1}
assert.Equal(t, "map[string]int{\"a\": 1}", 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},
},
}
assert.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}
assert.Equal(t, `[]uint8{1, 2, 3}`, String(b))
}
type privateTestStruct struct {
a string
}
func TestReprPrivateField(t *testing.T) {
s := privateTestStruct{"hello"}
assert.Equal(t, `repr.privateTestStruct{a: "hello"}`, String(s))
}
type Enum int
func (e Enum) String() string {
return "Value"
}
func TestEnum(t *testing.T) {
v := Enum(1)
s := String(v)
assert.Equal(t, "repr.Enum(Value)", s)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment