435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
328 lines
7.1 KiB
Go
328 lines
7.1 KiB
Go
package pretty
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strconv"
|
|
"text/tabwriter"
|
|
|
|
"github.com/kr/text"
|
|
)
|
|
|
|
type formatter struct {
|
|
v reflect.Value
|
|
force bool
|
|
quote bool
|
|
}
|
|
|
|
// Formatter makes a wrapper, f, that will format x as go source with line
|
|
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
|
// "#" and " " (space) flags are set, for example:
|
|
//
|
|
// fmt.Sprintf("%# v", Formatter(x))
|
|
//
|
|
// If one of these two flags is not set, or any other verb is used, f will
|
|
// format x according to the usual rules of package fmt.
|
|
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
|
func Formatter(x interface{}) (f fmt.Formatter) {
|
|
return formatter{v: reflect.ValueOf(x), quote: true}
|
|
}
|
|
|
|
func (fo formatter) String() string {
|
|
return fmt.Sprint(fo.v.Interface()) // unwrap it
|
|
}
|
|
|
|
func (fo formatter) passThrough(f fmt.State, c rune) {
|
|
s := "%"
|
|
for i := 0; i < 128; i++ {
|
|
if f.Flag(i) {
|
|
s += string(i)
|
|
}
|
|
}
|
|
if w, ok := f.Width(); ok {
|
|
s += fmt.Sprintf("%d", w)
|
|
}
|
|
if p, ok := f.Precision(); ok {
|
|
s += fmt.Sprintf(".%d", p)
|
|
}
|
|
s += string(c)
|
|
fmt.Fprintf(f, s, fo.v.Interface())
|
|
}
|
|
|
|
func (fo formatter) Format(f fmt.State, c rune) {
|
|
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
|
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
|
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
|
|
p.printValue(fo.v, true, fo.quote)
|
|
w.Flush()
|
|
return
|
|
}
|
|
fo.passThrough(f, c)
|
|
}
|
|
|
|
type printer struct {
|
|
io.Writer
|
|
tw *tabwriter.Writer
|
|
visited map[visit]int
|
|
depth int
|
|
}
|
|
|
|
func (p *printer) indent() *printer {
|
|
q := *p
|
|
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
|
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
|
return &q
|
|
}
|
|
|
|
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
|
if showType {
|
|
io.WriteString(p, v.Type().String())
|
|
fmt.Fprintf(p, "(%#v)", x)
|
|
} else {
|
|
fmt.Fprintf(p, "%#v", x)
|
|
}
|
|
}
|
|
|
|
// printValue must keep track of already-printed pointer values to avoid
|
|
// infinite recursion.
|
|
type visit struct {
|
|
v uintptr
|
|
typ reflect.Type
|
|
}
|
|
|
|
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
|
if p.depth > 10 {
|
|
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
|
|
return
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Bool:
|
|
p.printInline(v, v.Bool(), showType)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
p.printInline(v, v.Int(), showType)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
p.printInline(v, v.Uint(), showType)
|
|
case reflect.Float32, reflect.Float64:
|
|
p.printInline(v, v.Float(), showType)
|
|
case reflect.Complex64, reflect.Complex128:
|
|
fmt.Fprintf(p, "%#v", v.Complex())
|
|
case reflect.String:
|
|
p.fmtString(v.String(), quote)
|
|
case reflect.Map:
|
|
t := v.Type()
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
writeByte(p, '{')
|
|
if nonzero(v) {
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
keys := v.MapKeys()
|
|
for i := 0; i < v.Len(); i++ {
|
|
k := keys[i]
|
|
mv := v.MapIndex(k)
|
|
pp.printValue(k, false, true)
|
|
writeByte(pp, ':')
|
|
if expand {
|
|
writeByte(pp, '\t')
|
|
}
|
|
showTypeInStruct := t.Elem().Kind() == reflect.Interface
|
|
pp.printValue(mv, showTypeInStruct, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.Len()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Struct:
|
|
t := v.Type()
|
|
if v.CanAddr() {
|
|
addr := v.UnsafeAddr()
|
|
vis := visit{addr, t}
|
|
if vd, ok := p.visited[vis]; ok && vd < p.depth {
|
|
p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
|
|
break // don't print v again
|
|
}
|
|
p.visited[vis] = p.depth
|
|
}
|
|
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
writeByte(p, '{')
|
|
if nonzero(v) {
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
for i := 0; i < v.NumField(); i++ {
|
|
showTypeInStruct := true
|
|
if f := t.Field(i); f.Name != "" {
|
|
io.WriteString(pp, f.Name)
|
|
writeByte(pp, ':')
|
|
if expand {
|
|
writeByte(pp, '\t')
|
|
}
|
|
showTypeInStruct = labelType(f.Type)
|
|
}
|
|
pp.printValue(getField(v, i), showTypeInStruct, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.NumField()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Interface:
|
|
switch e := v.Elem(); {
|
|
case e.Kind() == reflect.Invalid:
|
|
io.WriteString(p, "nil")
|
|
case e.IsValid():
|
|
pp := *p
|
|
pp.depth++
|
|
pp.printValue(e, showType, true)
|
|
default:
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, "(nil)")
|
|
}
|
|
case reflect.Array, reflect.Slice:
|
|
t := v.Type()
|
|
if showType {
|
|
io.WriteString(p, t.String())
|
|
}
|
|
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
|
io.WriteString(p, "(nil)")
|
|
break
|
|
}
|
|
if v.Kind() == reflect.Slice && v.IsNil() {
|
|
io.WriteString(p, "nil")
|
|
break
|
|
}
|
|
writeByte(p, '{')
|
|
expand := !canInline(v.Type())
|
|
pp := p
|
|
if expand {
|
|
writeByte(p, '\n')
|
|
pp = p.indent()
|
|
}
|
|
for i := 0; i < v.Len(); i++ {
|
|
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
|
pp.printValue(v.Index(i), showTypeInSlice, true)
|
|
if expand {
|
|
io.WriteString(pp, ",\n")
|
|
} else if i < v.Len()-1 {
|
|
io.WriteString(pp, ", ")
|
|
}
|
|
}
|
|
if expand {
|
|
pp.tw.Flush()
|
|
}
|
|
writeByte(p, '}')
|
|
case reflect.Ptr:
|
|
e := v.Elem()
|
|
if !e.IsValid() {
|
|
writeByte(p, '(')
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, ")(nil)")
|
|
} else {
|
|
pp := *p
|
|
pp.depth++
|
|
writeByte(pp, '&')
|
|
pp.printValue(e, true, true)
|
|
}
|
|
case reflect.Chan:
|
|
x := v.Pointer()
|
|
if showType {
|
|
writeByte(p, '(')
|
|
io.WriteString(p, v.Type().String())
|
|
fmt.Fprintf(p, ")(%#v)", x)
|
|
} else {
|
|
fmt.Fprintf(p, "%#v", x)
|
|
}
|
|
case reflect.Func:
|
|
io.WriteString(p, v.Type().String())
|
|
io.WriteString(p, " {...}")
|
|
case reflect.UnsafePointer:
|
|
p.printInline(v, v.Pointer(), showType)
|
|
case reflect.Invalid:
|
|
io.WriteString(p, "nil")
|
|
}
|
|
}
|
|
|
|
func canInline(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Map:
|
|
return !canExpand(t.Elem())
|
|
case reflect.Struct:
|
|
for i := 0; i < t.NumField(); i++ {
|
|
if canExpand(t.Field(i).Type) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Interface:
|
|
return false
|
|
case reflect.Array, reflect.Slice:
|
|
return !canExpand(t.Elem())
|
|
case reflect.Ptr:
|
|
return false
|
|
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func canExpand(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Map, reflect.Struct,
|
|
reflect.Interface, reflect.Array, reflect.Slice,
|
|
reflect.Ptr:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func labelType(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Interface, reflect.Struct:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *printer) fmtString(s string, quote bool) {
|
|
if quote {
|
|
s = strconv.Quote(s)
|
|
}
|
|
io.WriteString(p, s)
|
|
}
|
|
|
|
func writeByte(w io.Writer, b byte) {
|
|
w.Write([]byte{b})
|
|
}
|
|
|
|
func getField(v reflect.Value, i int) reflect.Value {
|
|
val := v.Field(i)
|
|
if val.Kind() == reflect.Interface && !val.IsNil() {
|
|
val = val.Elem()
|
|
}
|
|
return val
|
|
}
|