194 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| // 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 xerrors
 | |
| 
 | |
| 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 {
 | |
| 				io.WriteString(&p.buf, 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:
 | |
| 			io.WriteString(&p.buf, 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
 | |
| }
 |