123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // Copyright 2018 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package errors
- import (
- "bytes"
- "fmt"
- "io"
- "reflect"
- "strconv"
- )
- // FormatError calls the FormatError method of f with an errors.Printer
- // configured according to s and verb, and writes the result to s.
- func FormatError(f Formatter, s fmt.State, verb rune) {
- // Assuming this function is only called from the Format method, and given
- // that FormatError takes precedence over Format, it cannot be called from
- // any package that supports errors.Formatter. It is therefore safe to
- // disregard that State may be a specific printer implementation and use one
- // of our choice instead.
- // limitations: does not support printing error as Go struct.
- var (
- sep = " " // separator before next error
- p = &state{State: s}
- direct = true
- )
- var err error = f
- switch verb {
- // Note that this switch must match the preference order
- // for ordinary string printing (%#v before %+v, and so on).
- case 'v':
- if s.Flag('#') {
- if stringer, ok := err.(fmt.GoStringer); ok {
- p.buf.WriteString(stringer.GoString())
- goto exit
- }
- // proceed as if it were %v
- } else if s.Flag('+') {
- p.printDetail = true
- sep = "\n - "
- }
- case 's':
- case 'q', 'x', 'X':
- // Use an intermediate buffer in the rare cases that precision,
- // truncation, or one of the alternative verbs (q, x, and X) are
- // specified.
- direct = false
- default:
- p.buf.WriteString("%!")
- p.buf.WriteRune(verb)
- p.buf.WriteByte('(')
- switch {
- case err != nil:
- p.buf.WriteString(reflect.TypeOf(f).String())
- default:
- p.buf.WriteString("<nil>")
- }
- p.buf.WriteByte(')')
- _, _ = io.Copy(s, &p.buf)
- return
- }
- loop:
- for {
- switch v := err.(type) {
- case Formatter:
- err = v.FormatError((*printer)(p))
- case fmt.Formatter:
- v.Format(p, 'v')
- break loop
- default:
- _, _ = p.buf.WriteString(v.Error())
- break loop
- }
- if err == nil {
- break
- }
- if p.needColon || !p.printDetail {
- p.buf.WriteByte(':')
- p.needColon = false
- }
- p.buf.WriteString(sep)
- p.inDetail = false
- p.needNewline = false
- }
- exit:
- width, okW := s.Width()
- prec, okP := s.Precision()
- if !direct || (okW && width > 0) || okP {
- // Construct format string from State s.
- format := []byte{'%'}
- if s.Flag('-') {
- format = append(format, '-')
- }
- if s.Flag('+') {
- format = append(format, '+')
- }
- if s.Flag(' ') {
- format = append(format, ' ')
- }
- if okW {
- format = strconv.AppendInt(format, int64(width), 10)
- }
- if okP {
- format = append(format, '.')
- format = strconv.AppendInt(format, int64(prec), 10)
- }
- format = append(format, string(verb)...)
- _, _ = fmt.Fprintf(s, string(format), p.buf.String())
- } else {
- _, _ = io.Copy(s, &p.buf)
- }
- }
- var detailSep = []byte("\n ")
- // state tracks error printing state. It implements fmt.State.
- type state struct {
- fmt.State
- buf bytes.Buffer
- printDetail bool
- inDetail bool
- needColon bool
- needNewline bool
- }
- func (s *state) Write(b []byte) (n int, err error) {
- if s.printDetail {
- if len(b) == 0 {
- return 0, nil
- }
- if s.inDetail && s.needColon {
- s.needNewline = true
- if b[0] == '\n' {
- b = b[1:]
- }
- }
- k := 0
- for i, c := range b {
- if s.needNewline {
- if s.inDetail && s.needColon {
- s.buf.WriteByte(':')
- s.needColon = false
- }
- s.buf.Write(detailSep)
- s.needNewline = false
- }
- if c == '\n' {
- s.buf.Write(b[k:i])
- k = i + 1
- s.needNewline = true
- }
- }
- s.buf.Write(b[k:])
- if !s.inDetail {
- s.needColon = true
- }
- } else if !s.inDetail {
- s.buf.Write(b)
- }
- return len(b), nil
- }
- // printer wraps a state to implement an xerrors.Printer.
- type printer state
- func (s *printer) Print(args ...interface{}) {
- if !s.inDetail || s.printDetail {
- _, _ = fmt.Fprint((*state)(s), args...)
- }
- }
- func (s *printer) Printf(format string, args ...interface{}) {
- if !s.inDetail || s.printDetail {
- _, _ = fmt.Fprintf((*state)(s), format, args...)
- }
- }
- func (s *printer) Detail() bool {
- s.inDetail = true
- return s.printDetail
- }
|