110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
|
// 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 (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/xerrors/internal"
|
||
|
)
|
||
|
|
||
|
// Errorf formats according to a format specifier and returns the string as a
|
||
|
// value that satisfies error.
|
||
|
//
|
||
|
// The returned error includes the file and line number of the caller when
|
||
|
// formatted with additional detail enabled. If the last argument is an error
|
||
|
// the returned error's Format method will return it if the format string ends
|
||
|
// with ": %s", ": %v", or ": %w". If the last argument is an error and the
|
||
|
// format string ends with ": %w", the returned error implements Wrapper
|
||
|
// with an Unwrap method returning it.
|
||
|
func Errorf(format string, a ...interface{}) error {
|
||
|
err, wrap := lastError(format, a)
|
||
|
format = formatPlusW(format)
|
||
|
if err == nil {
|
||
|
return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
|
||
|
}
|
||
|
|
||
|
// TODO: this is not entirely correct. The error value could be
|
||
|
// printed elsewhere in format if it mixes numbered with unnumbered
|
||
|
// substitutions. With relatively small changes to doPrintf we can
|
||
|
// have it optionally ignore extra arguments and pass the argument
|
||
|
// list in its entirety.
|
||
|
msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
|
||
|
frame := Frame{}
|
||
|
if internal.EnableTrace {
|
||
|
frame = Caller(1)
|
||
|
}
|
||
|
if wrap {
|
||
|
return &wrapError{msg, err, frame}
|
||
|
}
|
||
|
return &noWrapError{msg, err, frame}
|
||
|
}
|
||
|
|
||
|
// formatPlusW is used to avoid the vet check that will barf at %w.
|
||
|
func formatPlusW(s string) string {
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func lastError(format string, a []interface{}) (err error, wrap bool) {
|
||
|
wrap = strings.HasSuffix(format, ": %w")
|
||
|
if !wrap &&
|
||
|
!strings.HasSuffix(format, ": %s") &&
|
||
|
!strings.HasSuffix(format, ": %v") {
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
if len(a) == 0 {
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
err, ok := a[len(a)-1].(error)
|
||
|
if !ok {
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
return err, wrap
|
||
|
}
|
||
|
|
||
|
type noWrapError struct {
|
||
|
msg string
|
||
|
err error
|
||
|
frame Frame
|
||
|
}
|
||
|
|
||
|
func (e *noWrapError) Error() string {
|
||
|
return fmt.Sprint(e)
|
||
|
}
|
||
|
|
||
|
func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||
|
|
||
|
func (e *noWrapError) FormatError(p Printer) (next error) {
|
||
|
p.Print(e.msg)
|
||
|
e.frame.Format(p)
|
||
|
return e.err
|
||
|
}
|
||
|
|
||
|
type wrapError struct {
|
||
|
msg string
|
||
|
err error
|
||
|
frame Frame
|
||
|
}
|
||
|
|
||
|
func (e *wrapError) Error() string {
|
||
|
return fmt.Sprint(e)
|
||
|
}
|
||
|
|
||
|
func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||
|
|
||
|
func (e *wrapError) FormatError(p Printer) (next error) {
|
||
|
p.Print(e.msg)
|
||
|
e.frame.Format(p)
|
||
|
return e.err
|
||
|
}
|
||
|
|
||
|
func (e *wrapError) Unwrap() error {
|
||
|
return e.err
|
||
|
}
|