Skip to content

Commit c60a2e6

Browse files
authored
feat!: Close -> Do (#19)
* feat!: Close -> Do * update lint config
1 parent 5f56b93 commit c60a2e6

File tree

6 files changed

+43
-54
lines changed

6 files changed

+43
-54
lines changed

.golangci.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# https://golangci-lint.run/usage/configuration
2+
version: "2"
3+
4+
linters:
5+
default: standard
6+
enable:
7+
- gocritic
8+
settings:
9+
gocritic:
10+
enable-all: true
11+
exclusions:
12+
presets:
13+
- std-error-handling
14+
15+
formatters:
16+
enable:
17+
- gofumpt
18+
- goimports

.golangci.yml

Lines changed: 0 additions & 23 deletions
This file was deleted.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ if pathErr, ok := errorsx.As[*os.PathError](err); ok {
5050
}
5151
```
5252

53-
### Close
53+
### Do
5454

55-
Attempts to close the given `io.Closer` and assigns the returned error (if any) to `err`.
55+
Calls the given function and joins the returned error (if any) with `err`.
5656

5757
```go
5858
f, err := os.Open("file.txt")
@@ -66,5 +66,5 @@ defer func() {
6666
}()
6767

6868
// After:
69-
defer errorsx.Close(f, &err)
69+
defer errorsx.Do(f.Close, &err)
7070
```

errorsx.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// Package errorsx implements extensions for the standard [errors] package.
22
package errorsx
33

4-
import (
5-
"errors"
6-
"io"
7-
)
4+
import "errors"
85

96
// IsAny is a multi-target version of [errors.Is].
107
func IsAny(err, target error, targets ...error) bool {
@@ -26,8 +23,9 @@ func As[T any](err error) (T, bool) {
2623
return t, ok
2724
}
2825

29-
// Close attempts to close the given [io.Closer] and assigns the returned error (if any) to err.
30-
// If err is already not nil, it will be joined with the [io.Closer]'s error.
31-
func Close(c io.Closer, err *error) { //nolint:gocritic // ptrToRefParam: err must be a pointer here.
32-
*err = errors.Join(*err, c.Close())
26+
// Do calls the given function and joins the returned error (if any) with err.
27+
// Do is meant to be used in defer statements to record errors returned by [os.File.Close], [sql.Tx.Rollback], etc.
28+
// Note that the returned error in the function where Do is deferred must be named.
29+
func Do(fn func() error, err *error) { //nolint:gocritic // ptrToRefParam: err must be a pointer here.
30+
*err = errors.Join(*err, fn())
3331
}

errorsx_test.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestIsAny(t *testing.T) {
1414
targets []error
1515
want bool
1616
}{
17-
"no matches": {err: errFoo, targets: []error{errBar}, want: false},
17+
"no match": {err: errFoo, targets: []error{errBar}, want: false},
1818
"single target match": {err: errFoo, targets: []error{errFoo}, want: true},
1919
"single target match (wrapped)": {err: wrap(errFoo), targets: []error{errFoo}, want: true},
2020
"multiple targets match (wrapped)": {err: wrap(errFoo), targets: []error{errBar, errFoo}, want: true},
@@ -33,41 +33,41 @@ func TestAs(t *testing.T) {
3333
isok := func(_ any, ok bool) bool { return ok }
3434

3535
tests := map[string]struct {
36-
fn func(error) bool
3736
err error
37+
as func(error) bool
3838
want bool
3939
}{
40-
"no match": {fn: func(err error) bool { return isok(errorsx.As[barError](err)) }, err: errFoo, want: false},
41-
"match (exact)": {fn: func(err error) bool { return isok(errorsx.As[fooError](err)) }, err: errFoo, want: true},
42-
"match (wrapped)": {fn: func(err error) bool { return isok(errorsx.As[fooError](err)) }, err: wrap(errFoo), want: true},
40+
"no match": {err: errFoo, as: func(err error) bool { return isok(errorsx.As[barError](err)) }, want: false},
41+
"match (exact)": {err: errFoo, as: func(err error) bool { return isok(errorsx.As[fooError](err)) }, want: true},
42+
"match (wrapped)": {err: wrap(errFoo), as: func(err error) bool { return isok(errorsx.As[fooError](err)) }, want: true},
4343
}
4444

4545
for name, test := range tests {
4646
t.Run(name, func(t *testing.T) {
47-
if got := test.fn(test.err); got != test.want {
47+
if got := test.as(test.err); got != test.want {
4848
t.Errorf("got %t; want %t", got, test.want)
4949
}
5050
})
5151
}
5252
}
5353

54-
func TestClose(t *testing.T) {
54+
func TestDo(t *testing.T) {
5555
tests := map[string]struct {
5656
mainErr error
57-
closeErr error
57+
deferErr error
5858
wantErrs []error
5959
}{
60-
"main: ok; close: ok": {mainErr: nil, closeErr: nil, wantErrs: []error{}},
61-
"main: ok; close: error": {mainErr: nil, closeErr: errBar, wantErrs: []error{errBar}},
62-
"main: error; close: ok": {mainErr: errFoo, closeErr: nil, wantErrs: []error{errFoo}},
63-
"main: error; close: error": {mainErr: errFoo, closeErr: errBar, wantErrs: []error{errFoo, errBar}},
60+
"main: ok; defer: ok": {mainErr: nil, deferErr: nil, wantErrs: []error{}},
61+
"main: ok; defer: error": {mainErr: nil, deferErr: errBar, wantErrs: []error{errBar}},
62+
"main: error; defer: ok": {mainErr: errFoo, deferErr: nil, wantErrs: []error{errFoo}},
63+
"main: error; defer: error": {mainErr: errFoo, deferErr: errBar, wantErrs: []error{errFoo, errBar}},
6464
}
6565

6666
for name, test := range tests {
6767
t.Run(name, func(t *testing.T) {
6868
gotErr := func() (err error) {
69-
c := errCloser{err: test.closeErr}
70-
defer errorsx.Close(&c, &err)
69+
fn := func() error { return test.deferErr }
70+
defer errorsx.Do(fn, &err)
7171
return test.mainErr
7272
}()
7373
for _, wantErr := range test.wantErrs {
@@ -92,8 +92,4 @@ type barError struct{}
9292

9393
func (barError) Error() string { return "bar" }
9494

95-
type errCloser struct{ err error }
96-
97-
func (c *errCloser) Close() error { return c.err }
98-
9995
func wrap(err error) error { return fmt.Errorf("%w", err) }

example_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ func ExampleAs() {
2121
}
2222
}
2323

24-
func ExampleClose() {
24+
func ExampleDo() {
2525
_ = func() (err error) {
2626
f, err := os.Open("file.txt")
2727
if err != nil {
2828
return err
2929
}
30-
defer errorsx.Close(f, &err)
30+
defer errorsx.Do(f.Close, &err)
3131

3232
return nil
3333
}

0 commit comments

Comments
 (0)