OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / errors / errors.go
1 package errors
2
3 import (
4         "errors"
5         "fmt"
6         "strings"
7 )
8
9 // New returns an error that formats as the given text.
10 func New(text string) error {
11         return errors.New(text)
12 }
13
14 // wrapperError satisfies the error interface.
15 type wrapperError struct {
16         msg    string
17         detail []string
18         data   map[string]interface{}
19         stack  []StackFrame
20         root   error
21 }
22
23 // It satisfies the error interface.
24 func (e wrapperError) Error() string {
25         return e.msg
26 }
27
28 // Root returns the original error that was wrapped by one or more
29 // calls to Wrap. If e does not wrap other errors, it will be returned
30 // as-is.
31 func Root(e error) error {
32         if wErr, ok := e.(wrapperError); ok {
33                 return wErr.root
34         }
35         return e
36 }
37
38 // wrap adds a context message and stack trace to err and returns a new error
39 // containing the new context. This function is meant to be composed within
40 // other exported functions, such as Wrap and WithDetail.
41 // The argument stackSkip is the number of stack frames to ascend when
42 // generating stack straces, where 0 is the caller of wrap.
43 func wrap(err error, msg string, stackSkip int) error {
44         if err == nil {
45                 return nil
46         }
47
48         werr, ok := err.(wrapperError)
49         if !ok {
50                 werr.root = err
51                 werr.msg = err.Error()
52                 werr.stack = getStack(stackSkip+2, stackTraceSize)
53         }
54         if msg != "" {
55                 werr.msg = msg + ": " + werr.msg
56         }
57
58         return werr
59 }
60
61 // Wrap adds a context message and stack trace to err and returns a new error
62 // with the new context. Arguments are handled as in fmt.Print.
63 // Use Root to recover the original error wrapped by one or more calls to Wrap.
64 // Use Stack to recover the stack trace.
65 // Wrap returns nil if err is nil.
66 func Wrap(err error, a ...interface{}) error {
67         if err == nil {
68                 return nil
69         }
70         return wrap(err, fmt.Sprint(a...), 1)
71 }
72
73 // Wrapf is like Wrap, but arguments are handled as in fmt.Printf.
74 func Wrapf(err error, format string, a ...interface{}) error {
75         if err == nil {
76                 return nil
77         }
78         return wrap(err, fmt.Sprintf(format, a...), 1)
79 }
80
81 // WithDetail returns a new error that wraps
82 // err as a chain error messsage containing text
83 // as its additional context.
84 // Function Detail will return the given text
85 // when called on the new error value.
86 func WithDetail(err error, text string) error {
87         if err == nil {
88                 return nil
89         }
90         if text == "" {
91                 return err
92         }
93         e1 := wrap(err, text, 1).(wrapperError)
94         e1.detail = append(e1.detail, text)
95         return e1
96 }
97
98 // WithDetailf is like WithDetail, except it formats
99 // the detail message as in fmt.Printf.
100 // Function Detail will return the formatted text
101 // when called on the new error value.
102 func WithDetailf(err error, format string, v ...interface{}) error {
103         if err == nil {
104                 return nil
105         }
106         text := fmt.Sprintf(format, v...)
107         e1 := wrap(err, text, 1).(wrapperError)
108         e1.detail = append(e1.detail, text)
109         return e1
110 }
111
112 // Detail returns the detail message contained in err, if any.
113 // An error has a detail message if it was made by WithDetail
114 // or WithDetailf.
115 func Detail(err error) string {
116         wrapper, ok := err.(wrapperError)
117         if !ok {
118                 return err.Error()
119         }
120         return strings.Join(wrapper.detail, "; ")
121 }
122
123 // withData returns a new error that wraps err
124 // as a chain error message containing v as
125 // an extra data item.
126 // Calling Data on the returned error yields v.
127 // Note that if err already has a data item,
128 // it will not be accessible via the returned error value.
129 func withData(err error, v map[string]interface{}) error {
130         if err == nil {
131                 return nil
132         }
133         e1 := wrap(err, "", 1).(wrapperError)
134         e1.data = v
135         return e1
136 }
137
138 // WithData returns a new error that wraps err
139 // as a chain error message containing a value of type
140 // map[string]interface{} as an extra data item.
141 // The map contains the values in the map in err,
142 // if any, plus the items in keyval.
143 // Keyval takes the form
144 //   k1, v1, k2, v2, ...
145 // Values kN must be strings.
146 // Calling Data on the returned error yields the map.
147 // Note that if err already has a data item of any other type,
148 // it will not be accessible via the returned error value.
149 func WithData(err error, keyval ...interface{}) error {
150         if err == nil {
151                 return nil
152         }
153         // TODO(kr): add vet check for odd-length keyval and non-string keys
154         newkv := make(map[string]interface{})
155         for k, v := range Data(err) {
156                 newkv[k] = v
157         }
158         for i := 0; i < len(keyval); i += 2 {
159                 newkv[keyval[i].(string)] = keyval[i+1]
160         }
161         return withData(err, newkv)
162 }
163
164 // Data returns the data item in err, if any.
165 func Data(err error) map[string]interface{} {
166         wrapper, _ := err.(wrapperError)
167         return wrapper.data
168 }
169
170 // Sub returns an error containing root as its root and
171 // taking all other metadata (stack trace, detail, message,
172 // and data items) from err.
173 //
174 // Sub returns nil when either root or err is nil.
175 //
176 // Use this when you need to substitute a new root error in place
177 // of an existing error that may already hold a stack trace
178 // or other metadata.
179 func Sub(root, err error) error {
180         if wrapper, ok := err.(wrapperError); ok && root != nil {
181                 wrapper.root = Root(root)
182                 wrapper.msg = root.Error()
183                 root = wrapper
184         }
185         if err == nil {
186                 return nil
187         }
188         return Wrap(root, err.Error())
189 }