adaptor.go raw

   1  // Copyright 2018 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package xerrors
   6  
   7  import (
   8  	"bytes"
   9  	"fmt"
  10  	"io"
  11  	"reflect"
  12  	"strconv"
  13  )
  14  
  15  // FormatError calls the FormatError method of f with an errors.Printer
  16  // configured according to s and verb, and writes the result to s.
  17  func FormatError(f Formatter, s fmt.State, verb rune) {
  18  	// Assuming this function is only called from the Format method, and given
  19  	// that FormatError takes precedence over Format, it cannot be called from
  20  	// any package that supports errors.Formatter. It is therefore safe to
  21  	// disregard that State may be a specific printer implementation and use one
  22  	// of our choice instead.
  23  
  24  	// limitations: does not support printing error as Go struct.
  25  
  26  	var (
  27  		sep    = " " // separator before next error
  28  		p      = &state{State: s}
  29  		direct = true
  30  	)
  31  
  32  	var err error = f
  33  
  34  	switch verb {
  35  	// Note that this switch must match the preference order
  36  	// for ordinary string printing (%#v before %+v, and so on).
  37  
  38  	case 'v':
  39  		if s.Flag('#') {
  40  			if stringer, ok := err.(fmt.GoStringer); ok {
  41  				io.WriteString(&p.buf, stringer.GoString())
  42  				goto exit
  43  			}
  44  			// proceed as if it were %v
  45  		} else if s.Flag('+') {
  46  			p.printDetail = true
  47  			sep = "\n  - "
  48  		}
  49  	case 's':
  50  	case 'q', 'x', 'X':
  51  		// Use an intermediate buffer in the rare cases that precision,
  52  		// truncation, or one of the alternative verbs (q, x, and X) are
  53  		// specified.
  54  		direct = false
  55  
  56  	default:
  57  		p.buf.WriteString("%!")
  58  		p.buf.WriteRune(verb)
  59  		p.buf.WriteByte('(')
  60  		switch {
  61  		case err != nil:
  62  			p.buf.WriteString(reflect.TypeOf(f).String())
  63  		default:
  64  			p.buf.WriteString("<nil>")
  65  		}
  66  		p.buf.WriteByte(')')
  67  		io.Copy(s, &p.buf)
  68  		return
  69  	}
  70  
  71  loop:
  72  	for {
  73  		switch v := err.(type) {
  74  		case Formatter:
  75  			err = v.FormatError((*printer)(p))
  76  		case fmt.Formatter:
  77  			v.Format(p, 'v')
  78  			break loop
  79  		default:
  80  			io.WriteString(&p.buf, v.Error())
  81  			break loop
  82  		}
  83  		if err == nil {
  84  			break
  85  		}
  86  		if p.needColon || !p.printDetail {
  87  			p.buf.WriteByte(':')
  88  			p.needColon = false
  89  		}
  90  		p.buf.WriteString(sep)
  91  		p.inDetail = false
  92  		p.needNewline = false
  93  	}
  94  
  95  exit:
  96  	width, okW := s.Width()
  97  	prec, okP := s.Precision()
  98  
  99  	if !direct || (okW && width > 0) || okP {
 100  		// Construct format string from State s.
 101  		format := []byte{'%'}
 102  		if s.Flag('-') {
 103  			format = append(format, '-')
 104  		}
 105  		if s.Flag('+') {
 106  			format = append(format, '+')
 107  		}
 108  		if s.Flag(' ') {
 109  			format = append(format, ' ')
 110  		}
 111  		if okW {
 112  			format = strconv.AppendInt(format, int64(width), 10)
 113  		}
 114  		if okP {
 115  			format = append(format, '.')
 116  			format = strconv.AppendInt(format, int64(prec), 10)
 117  		}
 118  		format = append(format, string(verb)...)
 119  		fmt.Fprintf(s, string(format), p.buf.String())
 120  	} else {
 121  		io.Copy(s, &p.buf)
 122  	}
 123  }
 124  
 125  var detailSep = []byte("\n    ")
 126  
 127  // state tracks error printing state. It implements fmt.State.
 128  type state struct {
 129  	fmt.State
 130  	buf bytes.Buffer
 131  
 132  	printDetail bool
 133  	inDetail    bool
 134  	needColon   bool
 135  	needNewline bool
 136  }
 137  
 138  func (s *state) Write(b []byte) (n int, err error) {
 139  	if s.printDetail {
 140  		if len(b) == 0 {
 141  			return 0, nil
 142  		}
 143  		if s.inDetail && s.needColon {
 144  			s.needNewline = true
 145  			if b[0] == '\n' {
 146  				b = b[1:]
 147  			}
 148  		}
 149  		k := 0
 150  		for i, c := range b {
 151  			if s.needNewline {
 152  				if s.inDetail && s.needColon {
 153  					s.buf.WriteByte(':')
 154  					s.needColon = false
 155  				}
 156  				s.buf.Write(detailSep)
 157  				s.needNewline = false
 158  			}
 159  			if c == '\n' {
 160  				s.buf.Write(b[k:i])
 161  				k = i + 1
 162  				s.needNewline = true
 163  			}
 164  		}
 165  		s.buf.Write(b[k:])
 166  		if !s.inDetail {
 167  			s.needColon = true
 168  		}
 169  	} else if !s.inDetail {
 170  		s.buf.Write(b)
 171  	}
 172  	return len(b), nil
 173  }
 174  
 175  // printer wraps a state to implement an xerrors.Printer.
 176  type printer state
 177  
 178  func (s *printer) Print(args ...interface{}) {
 179  	if !s.inDetail || s.printDetail {
 180  		fmt.Fprint((*state)(s), args...)
 181  	}
 182  }
 183  
 184  func (s *printer) Printf(format string, args ...interface{}) {
 185  	if !s.inDetail || s.printDetail {
 186  		fmt.Fprintf((*state)(s), format, args...)
 187  	}
 188  }
 189  
 190  func (s *printer) Detail() bool {
 191  	s.inDetail = true
 192  	return s.printDetail
 193  }
 194