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 40 num := runtime.Callers(2, trace[:]) 41 traces := make([]Call, num) 42 43 for i := 0; i < num; i++ { 44 f := runtime.FuncForPC(trace[i]) 45 file, ln := f.FileLine(trace[i]) 46 traces[i] = Call{ 47 File: file, 48 Function: f.Name(), 49 LineNum: ln, 50 } 51 } 52 53 return &Trace{ 54 error: e, 55 Traces: traces, 56 } 57 } 58 59 // Trace returns a byte slice containing a description of the call stack. 60 func (t Trace) Trace() []byte { 61 var buf []byte 62 63 for _, c := range t.Traces { 64 buf = append(buf, c.File...) 65 buf = append(buf, ':', ' ', '(') 66 buf = append(buf, itobs(c.LineNum)...) 67 buf = append(buf, ')', ' ') 68 buf = append(buf, c.Function...) 69 buf = append(buf, '\n') 70 } 71 72 return buf 73 } 74 75 // Wrapper is used to get the underlying error for a wrapped error. 76 type Wrapper interface { 77 Unwrap() error 78 } 79 80 // Unwrap repeatedly checks for an underlying error to returned the original 81 // wrapped error. 82 func Unwrap(err error) error { 83 for { 84 if err == nil { 85 return nil 86 } 87 88 u, ok := err.(Wrapper) 89 if !ok { 90 return err 91 } 92 93 err = u.Unwrap() 94 } 95 } 96 97 // Unwrap returns the underlying error. 98 func (t Trace) Unwrap() error { 99 if u, ok := t.error.(Wrapper); ok { 100 return u.Unwrap() 101 } 102 103 return nil 104 } 105 106 func itobs(i int) []byte { 107 if i == 0 { 108 return []byte{'0'} 109 } 110 111 neg := false 112 113 if i < 0 { 114 neg = true 115 i = -i 116 } 117 118 pos := 21 119 120 var b [21]byte 121 122 for ; i > 0; i /= 10 { 123 pos-- 124 b[pos] = byte(i%10) + '0' 125 } 126 127 if neg { 128 pos-- 129 b[pos] = '-' 130 } 131 132 return b[pos:] 133 } 134 135 type contextual struct { 136 context string 137 error 138 } 139 140 // WithContext wraps an error, adding textural context to the error message. 141 // 142 // The wrapped error can be accessed via the Unwrap method. 143 // 144 // A nil error will not be wrapped. 145 func WithContext(context string, err error) error { 146 if err == nil { 147 return nil 148 } 149 150 return &contextual{ 151 context: context, 152 error: err, 153 } 154 } 155 156 func (c *contextual) Error() string { 157 return c.context + c.error.Error() 158 } 159 160 func (c *contextual) Unwrap() error { 161 return c.error 162 } 163