blob: 8b9a2304354565f7ddd9f645c1eb736f7a1cd2e1 [file] [log] [blame]
// Package errors implements functions to manipulate errors.
package errors
import (
"fmt"
"io"
"os"
"runtime"
"strings"
)
type loc uintptr
func (l loc) Location() (string, int) {
pc := uintptr(l)
fn := runtime.FuncForPC(pc)
if fn == nil {
return "unknown", 0
}
_, prefix, _, _ := runtime.Caller(0)
file, line := fn.FileLine(pc)
if i := strings.LastIndex(prefix, "github.com/pkg/errors"); i > 0 {
file = file[i:]
}
return file, line
}
// New returns an error that formats as the given text.
func New(text string) error {
pc, _, _, _ := runtime.Caller(1)
return struct {
error
loc
}{
fmt.Errorf(text),
loc(pc),
}
}
type e struct {
cause error
message string
loc
}
func (e *e) Error() string {
return e.message + ": " + e.cause.Error()
}
func (e *e) Cause() error {
return e.cause
}
// Wrap returns an error annotating the cause with message.
// If cause is nil, Wrap returns nil.
func Wrap(cause error, message string) error {
if cause == nil {
return nil
}
pc, _, _, _ := runtime.Caller(1)
return &e{
cause: cause,
message: message,
loc: loc(pc),
}
}
type causer interface {
Cause() error
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type Causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}
type locationer interface {
Location() (string, int)
}
// Print prints the error to Stderr.
// If the error implements the Causer interface described in Cause
// Print will recurse into the error's cause.
// If the error implements the inteface:
//
// type Location interface {
// Location() (file string, line int)
// }
//
// Print will also print the file and line of the error.
func Print(err error) {
Fprint(os.Stderr, err)
}
// Fprint prints the error to the supplied writer.
// The format of the output is the same as Print.
// If err is nil, nothing is printed.
func Fprint(w io.Writer, err error) {
for err != nil {
location, ok := err.(locationer)
if ok {
file, line := location.Location()
fmt.Fprintf(w, "%s:%d: ", file, line)
}
switch err := err.(type) {
case *e:
fmt.Fprintln(w, err.message)
default:
fmt.Fprintln(w, err.Error())
}
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
}