• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

errdata/H17-Jun-2019-

v2/H13-Mar-2021-

.gitignoreH A D17-Jun-20198

AUTHORSH A D17-Jun-2019134

LICENSEH A D17-Jun-20191 KiB

README.mdH A D17-Jun-20195.7 KiB

errs.goH A D17-Jun-20196.9 KiB

errs_test.goH A D17-Jun-20196.4 KiB

go.modH A D17-Jun-201938

group.goH A D17-Jun-20192.2 KiB

group_test.goH A D17-Jun-2019780

README.md

1# errs
2
3[![GoDoc](https://godoc.org/github.com/zeebo/errs?status.svg)](https://godoc.org/github.com/zeebo/errs)
4[![Sourcegraph](https://sourcegraph.com/github.com/zeebo/errs/-/badge.svg)](https://sourcegraph.com/github.com/zeebo/errs?badge)
5[![Go Report Card](https://goreportcard.com/badge/github.com/zeebo/errs)](https://goreportcard.com/report/github.com/zeebo/errs)
6
7errs is a package for making errors friendly and easy.
8
9### Creating Errors
10
11The easiest way to use it, is to use the package level [New][New] function.
12It's much like `fmt.Errorf`, but better. For example:
13
14```go
15func checkThing() error {
16	return errs.New("what's up with %q?", "zeebo")
17}
18```
19
20Why is it better? Errors come with a stack trace that is only printed
21when a `"+"` character is used in the format string. This should retain the
22benefits of being able to diagnose where and why errors happen, without all of
23the noise of printing a stack trace in every situation. For example:
24
25```go
26func doSomeRealWork() {
27	err := checkThing()
28	if err != nil {
29		fmt.Printf("%+v\n", err) // contains stack trace if it's a errs error.
30		fmt.Printf("%v\n", err)  // does not contain a stack trace
31		return
32	}
33}
34```
35
36### Error Classes
37
38You can create a [Class][Class] of errors and check if any error was created by
39that class. The class name is prefixed to all of the errors it creates. For example:
40
41```go
42var Unauthorized = errs.Class("unauthorized")
43
44func checkUser(username, password string) error {
45	if username != "zeebo" {
46		return Unauthorized.New("who is %q?", username)
47	}
48	if password != "hunter2" {
49		return Unauthorized.New("that's not a good password, jerkmo!")
50	}
51	return nil
52}
53
54func handleRequest() {
55	if err := checkUser("zeebo", "hunter3"); Unauthorized.Has(err) {
56		fmt.Println(err)
57	}
58
59	// output:
60	// unauthorized: that's not a good password, jerkmo!
61}
62```
63
64Classes can also [Wrap][ClassWrap] other errors, and errors may be wrapped
65multiple times. For example:
66
67```go
68var (
69	Error        = errs.Class("mypackage")
70	Unauthorized = errs.Class("unauthorized")
71)
72
73func deep3() error {
74	return fmt.Errorf("ouch")
75}
76
77func deep2() error {
78	return Unauthorized.Wrap(deep3())
79}
80
81func deep1() error {
82	return Error.Wrap(deep2())
83}
84
85func deep() {
86	fmt.Println(deep1())
87
88	// output:
89	// mypackage: unauthorized: ouch
90}
91```
92
93In the above example, both `Error.Has(deep1())` and `Unauthorized.Has(deep1())`
94would return `true`, and the stack trace would only be recorded once at the
95`deep2` call.
96
97In addition, when an error has been wrapped, wrapping it again with the same class will
98not do anything. For example:
99
100```go
101func doubleWrap() {
102	fmt.Println(Error.Wrap(Error.New("foo")))
103
104	// output:
105	// mypackage: foo
106}
107```
108
109This is to make it an easier decision if you should wrap or not (you should).
110
111### Utilities
112
113[Classes][Classes] is a helper function to get a slice of classes that an error
114has. The latest wrap is first in the slice. For example:
115
116```go
117func getClasses() {
118	classes := errs.Classes(deep1())
119	fmt.Println(classes[0] == &Error)
120	fmt.Println(classes[1] == &Unauthorized)
121
122	// output:
123	// true
124	// true
125}
126```
127
128Finally, a helper function, [Unwrap][Unwrap] is provided to get the
129wrapped error in cases where you might want to inspect details. For
130example:
131
132```go
133var Error = errs.Class("mypackage")
134
135func getHandle() (*os.File, error) {
136	fh, err := os.Open("neat_things")
137	if err != nil {
138		return nil, Error.Wrap(err)
139	}
140	return fh, nil
141}
142
143func checkForNeatThings() {
144	fh, err := getHandle()
145	if os.IsNotExist(errs.Unwrap(err)) {
146		panic("no neat things?!")
147	}
148	if err != nil {
149		panic("phew, at least there are neat things, even if i can't see them")
150	}
151	fh.Close()
152}
153```
154
155It knows about both the `Cause() error` and `Unwrap() error` methods that are
156often used in the community, and will call them as many times as possible.
157
158### Defer
159
160The package also provides [WrapP][WrapP] versions of [Wrap][Wrap] that are useful
161in defer contexts. For example:
162
163```go
164func checkDefer() (err error) {
165	defer Error.WrapP(&err)
166
167	fh, err := os.Open("secret_stash")
168	if err != nil {
169		return nil, err
170	}
171	return fh.Close()
172}
173```
174
175### Groups
176
177[Groups][Group] allow one to collect a set of errors. For example:
178
179```go
180func tonsOfErrors() error {
181	var group errs.Group
182	for _, work := range someWork {
183		group.Add(maybeErrors(work))
184	}
185	return group.Err()
186}
187```
188
189Some things to note:
190
191- The [Add][GroupAdd] method only adds to the group if the passed in error is non-nil.
192- The [Err][GroupErr] method returns an error only if non-nil errors have been added, and
193  additionally returns just the error if only one error was added. Thus, we always
194  have that if you only call `group.Add(err)`, then `group.Err() == err`.
195
196The returned error will format itself similarly:
197
198```go
199func groupFormat() {
200	var group errs.Group
201	group.Add(errs.New("first"))
202	group.Add(errs.New("second"))
203	err := group.Err()
204
205	fmt.Printf("%v\n", err)
206	fmt.Println()
207	fmt.Printf("%+v\n", err)
208
209	// output:
210	// first; second
211	//
212	// group:
213	// --- first
214	//     ... stack trace
215	// --- second
216	//     ... stack trace
217}
218```
219
220### Contributing
221
222errs is released under an MIT License. If you want to contribute, be sure to
223add yourself to the list in AUTHORS.
224
225[New]: https://godoc.org/github.com/zeebo/errs#New
226[Wrap]: https://godoc.org/github.com/zeebo/errs#Wrap
227[WrapP]: https://godoc.org/github.com/zeebo/errs#WrapP
228[Class]: https://godoc.org/github.com/zeebo/errs#Class
229[ClassNew]: https://godoc.org/github.com/zeebo/errs#Class.New
230[ClassWrap]: https://godoc.org/github.com/zeebo/errs#Class.Wrap
231[Unwrap]: https://godoc.org/github.com/zeebo/errs#Unwrap
232[Classes]: https://godoc.org/github.com/zeebo/errs#Classes
233[Group]: https://godoc.org/github.com/zeebo/errs#Group
234[GroupAdd]: https://godoc.org/github.com/zeebo/errs#Group.Add
235[GroupErr]: https://godoc.org/github.com/zeebo/errs#Group.Err
236