|  | 
|  | 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 | +		p.inDetail = false | 
|  | 74 | + | 
|  | 75 | +		switch v := err.(type) { | 
|  | 76 | +		case Formatter: | 
|  | 77 | +			err = v.FormatError((*printer)(p)) | 
|  | 78 | +		case fmt.Formatter: | 
|  | 79 | +			v.Format(p, 'v') | 
|  | 80 | +			break loop | 
|  | 81 | +		default: | 
|  | 82 | +			io.WriteString(&p.buf, v.Error()) | 
|  | 83 | +			break loop | 
|  | 84 | +		} | 
|  | 85 | +		if err == nil { | 
|  | 86 | +			break | 
|  | 87 | +		} | 
|  | 88 | +		if !p.inDetail || !p.printDetail { | 
|  | 89 | +			p.buf.WriteByte(':') | 
|  | 90 | +		} | 
|  | 91 | +		// Strip last newline of detail. | 
|  | 92 | +		if bytes.HasSuffix(p.buf.Bytes(), detailSep) { | 
|  | 93 | +			p.buf.Truncate(p.buf.Len() - len(detailSep)) | 
|  | 94 | +		} | 
|  | 95 | +		p.buf.WriteString(sep) | 
|  | 96 | +		p.inDetail = false | 
|  | 97 | +	} | 
|  | 98 | + | 
|  | 99 | +exit: | 
|  | 100 | +	width, okW := s.Width() | 
|  | 101 | +	prec, okP := s.Precision() | 
|  | 102 | + | 
|  | 103 | +	if !direct || (okW && width > 0) || okP { | 
|  | 104 | +		// Construct format string from State s. | 
|  | 105 | +		format := []byte{'%'} | 
|  | 106 | +		if s.Flag('-') { | 
|  | 107 | +			format = append(format, '-') | 
|  | 108 | +		} | 
|  | 109 | +		if s.Flag('+') { | 
|  | 110 | +			format = append(format, '+') | 
|  | 111 | +		} | 
|  | 112 | +		if s.Flag(' ') { | 
|  | 113 | +			format = append(format, ' ') | 
|  | 114 | +		} | 
|  | 115 | +		if okW { | 
|  | 116 | +			format = strconv.AppendInt(format, int64(width), 10) | 
|  | 117 | +		} | 
|  | 118 | +		if okP { | 
|  | 119 | +			format = append(format, '.') | 
|  | 120 | +			format = strconv.AppendInt(format, int64(prec), 10) | 
|  | 121 | +		} | 
|  | 122 | +		format = append(format, string(verb)...) | 
|  | 123 | +		fmt.Fprintf(s, string(format), p.buf.String()) | 
|  | 124 | +	} else { | 
|  | 125 | +		io.Copy(s, &p.buf) | 
|  | 126 | +	} | 
|  | 127 | +} | 
|  | 128 | + | 
|  | 129 | +var detailSep = []byte("\n    ") | 
|  | 130 | + | 
|  | 131 | +// state tracks error printing state. It implements fmt.State. | 
|  | 132 | +type state struct { | 
|  | 133 | +	fmt.State | 
|  | 134 | +	buf bytes.Buffer | 
|  | 135 | + | 
|  | 136 | +	printDetail bool | 
|  | 137 | +	inDetail    bool | 
|  | 138 | +	needNewline bool | 
|  | 139 | +} | 
|  | 140 | + | 
|  | 141 | +func (s *state) Write(b []byte) (n int, err error) { | 
|  | 142 | +	if s.printDetail { | 
|  | 143 | +		if len(b) == 0 { | 
|  | 144 | +			return 0, nil | 
|  | 145 | +		} | 
|  | 146 | +		if s.inDetail && s.needNewline { | 
|  | 147 | +			s.needNewline = false | 
|  | 148 | +			s.buf.WriteByte(':') | 
|  | 149 | +			s.buf.Write(detailSep) | 
|  | 150 | +			if b[0] == '\n' { | 
|  | 151 | +				b = b[1:] | 
|  | 152 | +			} | 
|  | 153 | +		} | 
|  | 154 | +		k := 0 | 
|  | 155 | +		for i, c := range b { | 
|  | 156 | +			if c == '\n' { | 
|  | 157 | +				s.buf.Write(b[k:i]) | 
|  | 158 | +				s.buf.Write(detailSep) | 
|  | 159 | +				k = i + 1 | 
|  | 160 | +			} | 
|  | 161 | +		} | 
|  | 162 | +		s.buf.Write(b[k:]) | 
|  | 163 | +		s.needNewline = !s.inDetail | 
|  | 164 | +	} else if !s.inDetail { | 
|  | 165 | +		s.buf.Write(b) | 
|  | 166 | +	} | 
|  | 167 | +	return len(b), nil | 
|  | 168 | +} | 
|  | 169 | + | 
|  | 170 | +// printer wraps a state to implement an xerrors.Printer. | 
|  | 171 | +type printer state | 
|  | 172 | + | 
|  | 173 | +func (s *printer) Print(args ...interface{}) { | 
|  | 174 | +	if !s.inDetail || s.printDetail { | 
|  | 175 | +		fmt.Fprint((*state)(s), args...) | 
|  | 176 | +	} | 
|  | 177 | +} | 
|  | 178 | + | 
|  | 179 | +func (s *printer) Printf(format string, args ...interface{}) { | 
|  | 180 | +	if !s.inDetail || s.printDetail { | 
|  | 181 | +		fmt.Fprintf((*state)(s), format, args...) | 
|  | 182 | +	} | 
|  | 183 | +} | 
|  | 184 | + | 
|  | 185 | +func (s *printer) Detail() bool { | 
|  | 186 | +	s.inDetail = true | 
|  | 187 | +	return s.printDetail | 
|  | 188 | +} | 
0 commit comments