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