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