1 // Package errors is a simple package with a few error related types 2 package errors // import "vimagination.zapto.org/errors" 3 4 import "runtime" 5 6 // Error represents a constant error string 7 type Error string 8 9 // New returns an error that returns the given string 10 func New(str string) error { 11 return Error(str) 12 } 13 14 // Error returns the error string 15 func (e Error) Error() string { 16 return string(e) 17 } 18 19 // Call represents where a particular error was created 20 type Call struct { 21 File, Function string 22 LineNum int 23 } 24 25 // String returns a human-friendly representation of the call site 26 func (c Call) String() string { 27 return c.File + ": (" + string(itobs(c.LineNum)) + ")" + c.Function 28 } 29 30 // Trace represents the call stack for an error 31 type Trace struct { 32 error 33 Traces []Call 34 } 35 36 // AddTrace wraps an error with a call stack 37 func AddTrace(e error) error { 38 var trace [100]uintptr 39 num := runtime.Callers(2, trace[:]) 40 traces := make([]Call, num) 41 for i := 0; i < num; i++ { 42 f := runtime.FuncForPC(trace[i]) 43 file, ln := f.FileLine(trace[i]) 44 traces[i] = Call{ 45 File: file, 46 Function: f.Name(), 47 LineNum: ln, 48 } 49 } 50 return &Trace{ 51 error: e, 52 Traces: traces, 53 } 54 } 55 56 // Trace returns a byte slice containing a description of the call stack 57 func (t Trace) Trace() []byte { 58 var buf []byte 59 for _, c := range t.Traces { 60 buf = append(buf, c.File...) 61 buf = append(buf, ':', ' ', '(') 62 buf = append(buf, itobs(c.LineNum)...) 63 buf = append(buf, ')', ' ') 64 buf = append(buf, c.Function...) 65 buf = append(buf, '\n') 66 } 67 return buf 68 } 69 70 // Wrapper is used to get the underlying error for a wrapped error 71 type Wrapper interface { 72 Unwrap() error 73 } 74 75 // Unwrap repeatedly called checks for an underlying error to returned the 76 // original wrapped error. 77 func Unwrap(err error) error { 78 for { 79 if err == nil { 80 return err 81 } 82 u, ok := err.(Wrapper) 83 if !ok { 84 return err 85 } 86 err = u.Unwrap() 87 } 88 } 89 90 // Unwrap returns the underlying error 91 func (t Trace) Unwrap() error { 92 if u, ok := t.error.(Wrapper); ok { 93 return u.Unwrap() 94 } 95 return nil 96 } 97 98 func itobs(i int) []byte { 99 if i == 0 { 100 return []byte{'0'} 101 } 102 var neg = false 103 if i < 0 { 104 neg = true 105 i = -i 106 } 107 pos := 21 108 var b [21]byte 109 for ; i > 0; i /= 10 { 110 pos-- 111 b[pos] = byte(i%10) + '0' 112 } 113 if neg { 114 pos-- 115 b[pos] = '-' 116 } 117 return b[pos:] 118 } 119 120 type contextual struct { 121 context string 122 error 123 } 124 125 // WithContext wraps an error, adding textural context to the error message. 126 // 127 // The wrapped error can be accessed via the Unwrap method. 128 // 129 // A nil error will not be wrapped 130 func WithContext(context string, err error) error { 131 if err == nil { 132 return nil 133 } 134 return &contextual{ 135 context: context, 136 error: err, 137 } 138 } 139 140 func (c *contextual) Error() string { 141 return c.context + c.error.Error() 142 } 143 144 func (c *contextual) Unwrap() error { 145 return c.error 146 }