mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-02 20:44:13 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			328 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
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
 | 
						|
}
 |