github.com/ecadlabs/pretty@v0.0.0-20230412123216-0f3d25fb750b/formatter.go (about)

     1  package pretty
     2  
     3  import (
     4  	"encoding"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"strconv"
     9  	"text/tabwriter"
    10  
    11  	"github.com/kr/text"
    12  	"github.com/rogpeppe/go-internal/fmtsort"
    13  )
    14  
    15  type options struct {
    16  	goStringer    bool
    17  	stringer      bool
    18  	textMarshaler bool
    19  	maxDepth      int
    20  }
    21  
    22  var defaultOptions = options{goStringer: true, maxDepth: 10}
    23  
    24  type Option func(opt *options)
    25  
    26  func OptGoStringer(x bool) Option    { return func(opt *options) { opt.goStringer = x } }
    27  func OptStringer(x bool) Option      { return func(opt *options) { opt.stringer = x } }
    28  func OptTextMarshaler(x bool) Option { return func(opt *options) { opt.textMarshaler = x } }
    29  func OptMaxDepth(x int) Option       { return func(opt *options) { opt.maxDepth = x } }
    30  
    31  func makeOpt(opts []Option) options {
    32  	out := defaultOptions
    33  	for _, fn := range opts {
    34  		fn(&out)
    35  	}
    36  	return out
    37  }
    38  
    39  type formatter struct {
    40  	v     reflect.Value
    41  	force bool
    42  	quote bool
    43  	opt   options
    44  }
    45  
    46  // Formatter makes a wrapper, f, that will format x as go source with line
    47  // breaks and tabs. Object f responds to the "%v" formatting verb when both the
    48  // "#" and " " (space) flags are set, for example:
    49  //
    50  //	fmt.Sprintf("%# v", Formatter(x))
    51  //
    52  // If one of these two flags is not set, or any other verb is used, f will
    53  // format x according to the usual rules of package fmt.
    54  // In particular, if x satisfies fmt.Formatter, then x.Format will be called.
    55  func Formatter(x interface{}, opt ...Option) (f fmt.Formatter) {
    56  	return formatter{v: reflect.ValueOf(x), quote: true, opt: makeOpt(opt)}
    57  }
    58  
    59  func (fo formatter) String() string {
    60  	return fmt.Sprint(fo.v.Interface()) // unwrap it
    61  }
    62  
    63  func (fo formatter) passThrough(f fmt.State, c rune) {
    64  	s := "%"
    65  	for i := 0; i < 128; i++ {
    66  		if f.Flag(i) {
    67  			s += string(rune(i))
    68  		}
    69  	}
    70  	if w, ok := f.Width(); ok {
    71  		s += fmt.Sprintf("%d", w)
    72  	}
    73  	if p, ok := f.Precision(); ok {
    74  		s += fmt.Sprintf(".%d", p)
    75  	}
    76  	s += string(c)
    77  	fmt.Fprintf(f, s, fo.v.Interface())
    78  }
    79  
    80  func (fo formatter) Format(f fmt.State, c rune) {
    81  	if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
    82  		w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
    83  		p := &printer{tw: w, Writer: w, visited: make(map[visit]int), opt: &fo.opt}
    84  		p.printValue(fo.v, true, fo.quote)
    85  		w.Flush()
    86  		return
    87  	}
    88  	fo.passThrough(f, c)
    89  }
    90  
    91  type printer struct {
    92  	io.Writer
    93  	tw      *tabwriter.Writer
    94  	visited map[visit]int
    95  	depth   int
    96  	opt     *options
    97  }
    98  
    99  func (p *printer) indent() *printer {
   100  	q := *p
   101  	q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
   102  	q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
   103  	return &q
   104  }
   105  
   106  func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
   107  	if showType {
   108  		io.WriteString(p, v.Type().String())
   109  		fmt.Fprintf(p, "(%#v)", x)
   110  	} else {
   111  		fmt.Fprintf(p, "%#v", x)
   112  	}
   113  }
   114  
   115  // printValue must keep track of already-printed pointer values to avoid
   116  // infinite recursion.
   117  type visit struct {
   118  	v   uintptr
   119  	typ reflect.Type
   120  }
   121  
   122  func (p *printer) catchPanic(v reflect.Value, method string) {
   123  	if r := recover(); r != nil {
   124  		if v.Kind() == reflect.Ptr && v.IsNil() {
   125  			writeByte(p, '(')
   126  			io.WriteString(p, v.Type().String())
   127  			io.WriteString(p, ")(nil)")
   128  			return
   129  		}
   130  		writeByte(p, '(')
   131  		io.WriteString(p, v.Type().String())
   132  		io.WriteString(p, ")(PANIC=calling method ")
   133  		io.WriteString(p, strconv.Quote(method))
   134  		io.WriteString(p, ": ")
   135  		fmt.Fprint(p, r)
   136  		writeByte(p, ')')
   137  	}
   138  }
   139  
   140  var (
   141  	goStringerType    = reflect.TypeOf((*fmt.GoStringer)(nil)).Elem()
   142  	stringerType      = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
   143  	textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
   144  )
   145  
   146  func (p *printer) printValue(v reflect.Value, showType, quote bool) {
   147  	if p.depth > p.opt.maxDepth {
   148  		io.WriteString(p, "!%v(DEPTH EXCEEDED)")
   149  		return
   150  	}
   151  
   152  	if v.IsValid() && v.CanInterface() {
   153  		t := v.Type()
   154  		if p.opt.goStringer {
   155  			var goStringer fmt.GoStringer
   156  			if t.Implements(goStringerType) {
   157  				goStringer = v.Interface().(fmt.GoStringer)
   158  			} else if reflect.PtrTo(t).Implements(goStringerType) && v.CanAddr() {
   159  				goStringer = v.Addr().Interface().(fmt.GoStringer)
   160  			}
   161  			if goStringer != nil {
   162  				defer p.catchPanic(v, "GoString")
   163  				io.WriteString(p, goStringer.GoString())
   164  				return
   165  			}
   166  		}
   167  
   168  		if p.opt.stringer {
   169  			var stringer fmt.Stringer
   170  			if t.Implements(stringerType) {
   171  				stringer = v.Interface().(fmt.Stringer)
   172  			} else if reflect.PtrTo(t).Implements(stringerType) && v.CanAddr() {
   173  				stringer = v.Addr().Interface().(fmt.Stringer)
   174  			}
   175  			if stringer != nil {
   176  				defer p.catchPanic(v, "String")
   177  				p.fmtString(stringer.String(), quote)
   178  				return
   179  			}
   180  		}
   181  
   182  		if p.opt.textMarshaler {
   183  			var textMarshaler encoding.TextMarshaler
   184  			if t.Implements(textMarshalerType) {
   185  				textMarshaler = v.Interface().(encoding.TextMarshaler)
   186  			} else if reflect.PtrTo(t).Implements(textMarshalerType) && v.CanAddr() {
   187  				textMarshaler = v.Addr().Interface().(encoding.TextMarshaler)
   188  			}
   189  			if textMarshaler != nil {
   190  				defer p.catchPanic(v, "MarshalText")
   191  				text, err := textMarshaler.MarshalText()
   192  				if err != nil {
   193  					panic(err)
   194  				}
   195  				p.fmtString(string(text), quote)
   196  				return
   197  			}
   198  		}
   199  	}
   200  
   201  	switch v.Kind() {
   202  	case reflect.Bool:
   203  		p.printInline(v, v.Bool(), showType)
   204  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   205  		p.printInline(v, v.Int(), showType)
   206  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   207  		p.printInline(v, v.Uint(), showType)
   208  	case reflect.Float32, reflect.Float64:
   209  		p.printInline(v, v.Float(), showType)
   210  	case reflect.Complex64, reflect.Complex128:
   211  		fmt.Fprintf(p, "%#v", v.Complex())
   212  	case reflect.String:
   213  		p.fmtString(v.String(), quote)
   214  	case reflect.Map:
   215  		t := v.Type()
   216  		if showType {
   217  			io.WriteString(p, t.String())
   218  		}
   219  		writeByte(p, '{')
   220  		if nonzero(v) {
   221  			expand := !canInline(v.Type())
   222  			pp := p
   223  			if expand {
   224  				writeByte(p, '\n')
   225  				pp = p.indent()
   226  			}
   227  			sm := fmtsort.Sort(v)
   228  			for i := 0; i < v.Len(); i++ {
   229  				k := sm.Key[i]
   230  				mv := sm.Value[i]
   231  				pp.printValue(k, false, true)
   232  				writeByte(pp, ':')
   233  				if expand {
   234  					writeByte(pp, '\t')
   235  				}
   236  				showTypeInStruct := t.Elem().Kind() == reflect.Interface
   237  				pp.printValue(mv, showTypeInStruct, true)
   238  				if expand {
   239  					io.WriteString(pp, ",\n")
   240  				} else if i < v.Len()-1 {
   241  					io.WriteString(pp, ", ")
   242  				}
   243  			}
   244  			if expand {
   245  				pp.tw.Flush()
   246  			}
   247  		}
   248  		writeByte(p, '}')
   249  	case reflect.Struct:
   250  		t := v.Type()
   251  		if v.CanAddr() {
   252  			addr := v.UnsafeAddr()
   253  			vis := visit{addr, t}
   254  			if vd, ok := p.visited[vis]; ok && vd < p.depth {
   255  				p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
   256  				break // don't print v again
   257  			}
   258  			p.visited[vis] = p.depth
   259  		}
   260  
   261  		if showType {
   262  			io.WriteString(p, t.String())
   263  		}
   264  		writeByte(p, '{')
   265  		if nonzero(v) {
   266  			expand := !canInline(v.Type())
   267  			pp := p
   268  			if expand {
   269  				writeByte(p, '\n')
   270  				pp = p.indent()
   271  			}
   272  			for i := 0; i < v.NumField(); i++ {
   273  				showTypeInStruct := true
   274  				if f := t.Field(i); f.Name != "" {
   275  					io.WriteString(pp, f.Name)
   276  					writeByte(pp, ':')
   277  					if expand {
   278  						writeByte(pp, '\t')
   279  					}
   280  					showTypeInStruct = labelType(f.Type)
   281  				}
   282  				pp.printValue(getField(v, i), showTypeInStruct, true)
   283  				if expand {
   284  					io.WriteString(pp, ",\n")
   285  				} else if i < v.NumField()-1 {
   286  					io.WriteString(pp, ", ")
   287  				}
   288  			}
   289  			if expand {
   290  				pp.tw.Flush()
   291  			}
   292  		}
   293  		writeByte(p, '}')
   294  	case reflect.Interface:
   295  		switch e := v.Elem(); {
   296  		case e.Kind() == reflect.Invalid:
   297  			io.WriteString(p, "nil")
   298  		case e.IsValid():
   299  			pp := *p
   300  			pp.depth++
   301  			pp.printValue(e, showType, true)
   302  		default:
   303  			io.WriteString(p, v.Type().String())
   304  			io.WriteString(p, "(nil)")
   305  		}
   306  	case reflect.Array, reflect.Slice:
   307  		t := v.Type()
   308  		if showType {
   309  			io.WriteString(p, t.String())
   310  		}
   311  		if v.Kind() == reflect.Slice && v.IsNil() && showType {
   312  			io.WriteString(p, "(nil)")
   313  			break
   314  		}
   315  		if v.Kind() == reflect.Slice && v.IsNil() {
   316  			io.WriteString(p, "nil")
   317  			break
   318  		}
   319  		writeByte(p, '{')
   320  		expand := !canInline(v.Type())
   321  		pp := p
   322  		if expand {
   323  			writeByte(p, '\n')
   324  			pp = p.indent()
   325  		}
   326  		for i := 0; i < v.Len(); i++ {
   327  			showTypeInSlice := t.Elem().Kind() == reflect.Interface
   328  			pp.printValue(v.Index(i), showTypeInSlice, true)
   329  			if expand {
   330  				io.WriteString(pp, ",\n")
   331  			} else if i < v.Len()-1 {
   332  				io.WriteString(pp, ", ")
   333  			}
   334  		}
   335  		if expand {
   336  			pp.tw.Flush()
   337  		}
   338  		writeByte(p, '}')
   339  	case reflect.Ptr:
   340  		e := v.Elem()
   341  		if !e.IsValid() {
   342  			writeByte(p, '(')
   343  			io.WriteString(p, v.Type().String())
   344  			io.WriteString(p, ")(nil)")
   345  		} else {
   346  			pp := *p
   347  			pp.depth++
   348  			writeByte(pp, '&')
   349  			pp.printValue(e, true, true)
   350  		}
   351  	case reflect.Chan:
   352  		x := v.Pointer()
   353  		if showType {
   354  			writeByte(p, '(')
   355  			io.WriteString(p, v.Type().String())
   356  			fmt.Fprintf(p, ")(%#v)", x)
   357  		} else {
   358  			fmt.Fprintf(p, "%#v", x)
   359  		}
   360  	case reflect.Func:
   361  		io.WriteString(p, v.Type().String())
   362  		io.WriteString(p, " {...}")
   363  	case reflect.UnsafePointer:
   364  		p.printInline(v, v.Pointer(), showType)
   365  	case reflect.Invalid:
   366  		io.WriteString(p, "nil")
   367  	}
   368  }
   369  
   370  func canInline(t reflect.Type) bool {
   371  	switch t.Kind() {
   372  	case reflect.Map:
   373  		return !canExpand(t.Elem())
   374  	case reflect.Struct:
   375  		for i := 0; i < t.NumField(); i++ {
   376  			if canExpand(t.Field(i).Type) {
   377  				return false
   378  			}
   379  		}
   380  		return true
   381  	case reflect.Interface:
   382  		return false
   383  	case reflect.Array, reflect.Slice:
   384  		return !canExpand(t.Elem())
   385  	case reflect.Ptr:
   386  		return false
   387  	case reflect.Chan, reflect.Func, reflect.UnsafePointer:
   388  		return false
   389  	}
   390  	return true
   391  }
   392  
   393  func canExpand(t reflect.Type) bool {
   394  	switch t.Kind() {
   395  	case reflect.Map, reflect.Struct,
   396  		reflect.Interface, reflect.Array, reflect.Slice,
   397  		reflect.Ptr:
   398  		return true
   399  	}
   400  	return false
   401  }
   402  
   403  func labelType(t reflect.Type) bool {
   404  	switch t.Kind() {
   405  	case reflect.Interface, reflect.Struct:
   406  		return true
   407  	}
   408  	return false
   409  }
   410  
   411  func (p *printer) fmtString(s string, quote bool) {
   412  	if quote {
   413  		s = strconv.Quote(s)
   414  	}
   415  	io.WriteString(p, s)
   416  }
   417  
   418  func writeByte(w io.Writer, b byte) {
   419  	w.Write([]byte{b})
   420  }
   421  
   422  func getField(v reflect.Value, i int) reflect.Value {
   423  	val := v.Field(i)
   424  	if val.Kind() == reflect.Interface && !val.IsNil() {
   425  		val = val.Elem()
   426  	}
   427  	return val
   428  }