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

..03-May-2022-

errdata/H13-Mar-2021-

.gitignoreH A D13-Mar-20218

AUTHORSH A D13-Mar-2021134

LICENSEH A D13-Mar-20211 KiB

README.mdH A D13-Mar-20215.7 KiB

errs.goH A D13-Mar-20214.3 KiB

errs_test.goH A D13-Mar-20214.5 KiB

go.modH A D13-Mar-202181

go.sumH A D13-Mar-2021165

group.goH A D13-Mar-20212.9 KiB

group_test.goH A D13-Mar-20211.5 KiB

README.md

1# errs/v2
2
3<p>
4  <a href="https://pkg.go.dev/github.com/zeebo/errs/v2"><img src="https://img.shields.io/badge/doc-reference-007d9b?logo=go&style=flat-square" alt="go.dev" /></a>
5  <a href="https://goreportcard.com/report/github.com/zeebo/errs"><img src="https://goreportcard.com/badge/github.com/zeebo/errs?style=flat-square" alt="Go Report Card" /></a>
6  <a href="https://sourcegraph.com/github.com/zeebo/errs?badge"><img src="https://sourcegraph.com/github.com/zeebo/errs/-/badge.svg?style=flat-square" alt="SourceGraph" /></a>
7</p>
8
9
10errs is a package for making errors friendly and easy.
11
12### Creating Errors
13
14The easiest way to use it, is to use the package level [Errorf][Errorf] function. It's much like  `fmt.Errorf`, but better. For example:
15
16```go
17func checkThing() error {
18	return errs.Errorf("what's up with %q?", "zeebo")
19}
20```
21
22Why is it better? Errors come with a stack trace that is only printed when a `"+"` character is used in the format string. This should retain the benefits of being able to diagnose where and why errors happen, without all of the noise of printing a stack trace in every situation. For example:
23
24```go
25func doSomeRealWork() {
26	err := checkThing()
27	if err != nil {
28		fmt.Printf("%+v\n", err) // contains stack trace if it's a errs error.
29		fmt.Printf("%v\n", err)  // does not contain a stack trace
30		return
31	}
32}
33```
34
35### Error Tags
36
37You can create a [Tag][Tag] for errors and check if any error has been associated with that tag. The tag is prefixed to all of the error strings it creates, and tags are just strings: two tags with the same contents are the same tag. For example:
38
39```go
40const Unauthorized = errs.Tag("unauthorized")
41
42func checkUser(username, password string) error {
43	if username != "zeebo" {
44		return Unauthorized.Errorf("who is %q?", username)
45	}
46	if password != "hunter2" {
47		return Unauthorized.Errorf("that's not a good password, jerkmo!")
48	}
49	return nil
50}
51
52func handleRequest() {
53	if err := checkUser("zeebo", "hunter3"); errors.Is(err, Unauthorized) {
54		fmt.Println(err)
55		fmt.Println(errors.Is(err, Tag("unauthorized"))))
56	}
57
58	// output:
59	// unauthorized: that's not a good password, jerkmo!
60	// true
61}
62```
63
64Tags can also [Wrap][TagWrap] other errors, and errors may be wrapped multiple times. For example:
65
66```go
67const (
68	Package      = errs.Tag("mypackage")
69	Unauthorized = errs.Tag("unauthorized")
70)
71
72func deep3() error {
73	return fmt.Errorf("ouch")
74}
75
76func deep2() error {
77	return Unauthorized.Wrap(deep3())
78}
79
80func deep1() error {
81	return Package.Wrap(deep2())
82}
83
84func deep() {
85	fmt.Println(deep1())
86
87	// output:
88	// mypackage: unauthorized: ouch
89}
90```
91
92In the above example, both `errors.Is(deep1(), Package)` and `errors.Is(deep1()), Unauthorized)` would return `true`, and the stack trace would only be recorded once at the `deep2` call.
93
94In addition, when an error has been wrapped, wrapping it again with the same tag will not do anything. For example:
95
96```go
97func doubleWrap() {
98	fmt.Println(Package.Wrap(Package.Errorf("foo")))
99
100	// output:
101	// mypackage: foo
102}
103```
104
105This is to make it an easier decision if you should wrap or not (you should).
106
107Tags will also be "hoisted" when they match an incoming error being wrapped with the `"%w"` format verb. For example:
108
109```go
110func hoistWrap() {
111	err1 := Package.Errorf("foo")
112	err2 := Package.Errorf("blah: %w", err)
113	fmt.Println(err1)
114	fmt.Println(err2)
115
116	// output:
117	// mypackage: foo
118	// mypackage: blah: foo
119}
120```
121
122This only works if the format string ends with `"%w"` and an error is the last argument.
123
124### Utilities
125
126[Tags][Tags] is a helper function to get a slice of tags that an error has. The latest wrap is first in the slice. For example:
127
128```go
129func getTags() {
130	tags := errs.Tags(deep1())
131	fmt.Println(tags[0] == Package)
132	fmt.Println(tags[1] == Unauthorized)
133
134	// output:
135	// true
136	// true
137}
138```
139
140If you don't have a tag available but don't really want to make an exported one but do want to have the error tagged for monitoring purposes, you can create a one of tag with the [Tagged][Tagged] helper:
141
142```go
143func oneOff() error {
144	fh, err := fh.Open("somewhere")
145	if err != nil {
146		return errs.Tagged("open", err)
147	}
148	return errs.Tagged("close", fh.Close())
149}
150```
151
152### Groups
153
154[Groups][Group] allow one to collect a set of errors. For example:
155
156```go
157func tonsOfErrors() error {
158	var group errs.Group
159	for _, work := range someWork {
160		group.Add(maybeErrors(work))
161	}
162	return group.Err()
163}
164```
165
166Some things to note:
167
168- The [Add][GroupAdd] method only adds to the group if the passed in error is non-nil.
169- The [Err][GroupErr] method returns an error only if non-nil errors have been added, and aditionally returns just the error if only one error was added. Thus, we always have that if you only call `group.Add(err)`, then `group.Err() == err`.
170
171The returned error will format itself similarly:
172
173```go
174func groupFormat() {
175	var group errs.Group
176	group.Add(errs.Errorf("first"))
177	group.Add(errs.Errorf("second"))
178	err := group.Err()
179
180	fmt.Printf("%v\n", err)
181	fmt.Println()
182	fmt.Printf("%+v\n", err)
183
184	// output:
185	// first; second
186	//
187	// group:
188	// --- first
189	// 	... stack trace
190	// --- second
191	// 	... stack trace
192}
193```
194
195### Contributing
196
197errs is released under an MIT License. If you want to contribute, be sure to add yourself to the list in AUTHORS.
198
199[Errorf]: https://godoc.org/github.com/zeebo/errs/v2#Errorf
200[Wrap]: https://godoc.org/github.com/zeebo/errs/v2#Wrap
201[Tag]: https://godoc.org/github.com/zeebo/errs/v2#Tag
202[Tagged]: https://godoc.org/github.com/zeebo/errs/v2#Tagged
203[TagWrap]: https://godoc.org/github.com/zeebo/errs/v2#Tag.Wrap
204[Tags]: https://godoc.org/github.com/zeebo/errs/v2#Tags
205[Group]: https://godoc.org/github.com/zeebo/errs/v2#Group
206[GroupAdd]: https://godoc.org/github.com/zeebo/errs/v2#Group.Add
207[GroupErr]: https://godoc.org/github.com/zeebo/errs/v2#Group.Err
208