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 5package xerrors 6 7import ( 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. 17func 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 71loop: 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 95exit: 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 125var detailSep = []byte("\n ") 126 127// state tracks error printing state. It implements fmt.State. 128type state struct { 129 fmt.State 130 buf bytes.Buffer 131 132 printDetail bool 133 inDetail bool 134 needColon bool 135 needNewline bool 136} 137 138func (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. 176type printer state 177 178func (s *printer) Print(args ...interface{}) { 179 if !s.inDetail || s.printDetail { 180 fmt.Fprint((*state)(s), args...) 181 } 182} 183 184func (s *printer) Printf(format string, args ...interface{}) { 185 if !s.inDetail || s.printDetail { 186 fmt.Fprintf((*state)(s), format, args...) 187 } 188} 189 190func (s *printer) Detail() bool { 191 s.inDetail = true 192 return s.printDetail 193} 194