Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: drop IsTimeout #16

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 13 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![goreportcard](https://goreportcard.com/badge/go-simpler.org/errorsx)](https://goreportcard.com/report/go-simpler.org/errorsx)
[![codecov](https://codecov.io/gh/go-simpler/errorsx/branch/main/graph/badge.svg)](https://codecov.io/gh/go-simpler/errorsx)

Extensions for the standard `errors` package
Extensions for the standard `errors` package.

## 📦 Install

Expand All @@ -21,7 +21,7 @@ A multi-target version of `errors.Is`.

```go
if errorsx.IsAny(err, os.ErrNotExist, os.ErrPermission) {
// handle error
fmt.Println(err)
}
```

Expand All @@ -32,7 +32,7 @@ It is equivalent to `errors.As` without the need to declare the target variable.

```go
if errorsx.HasType[*os.PathError](err) {
// handle error
fmt.Println(err)
}
```

Expand All @@ -43,18 +43,7 @@ If the given error was created differently, `Split` returns nil.

```go
if errs := errorsx.Split(err); errs != nil {
// handle errors
}
```

### IsTimeout

Reports whether the error was caused by timeout.
Unlike `os.IsTimeout`, it respects error wrapping.

```go
if errorsx.IsTimeout(err) {
// handle timeout
fmt.Println(errs)
}
```

Expand All @@ -63,9 +52,13 @@ if errorsx.IsTimeout(err) {
Attempts to close the given `io.Closer` and assigns the returned error (if any) to `err`.

```go
f, err := os.Open("file.txt")
if err != nil {
return err
}
defer errorsx.Close(f, &err)
func() (err error) {
f, err := os.Open("file.txt")
if err != nil {
return err
}
defer errorsx.Close(f, &err)

return nil
}()
```
15 changes: 2 additions & 13 deletions errorsx.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package errorsx provides extensions for the standard [errors] package.
// Package errorsx implements extensions for the standard [errors] package.
package errorsx

import (
Expand Down Expand Up @@ -36,20 +36,9 @@ func Split(err error) []error {
return u.Unwrap()
}

// IsTimeout reports whether the error was caused by timeout.
// Unlike [os.IsTimeout], it respects error wrapping.
func IsTimeout(err error) bool {
var t interface {
Timeout() bool
}
return errors.As(err, &t) && t.Timeout()
}

// Close attempts to close the given [io.Closer] and assigns the returned error (if any) to err.
// If err is already not nil, it will be joined with the [io.Closer]'s error.
//
// NOTE: Close is designed to be used ONLY as a defer statement.
func Close(c io.Closer, err *error) { //nolint:gocritic // ptrToRefParam: false-positive
func Close(c io.Closer, err *error) { //nolint:gocritic // ptrToRefParam: err must be a pointer here.
if cerr := c.Close(); cerr != nil {
*err = errors.Join(*err, cerr)
}
Expand Down
32 changes: 4 additions & 28 deletions errorsx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package errorsx_test
import (
"errors"
"fmt"
"os"
"slices"
"testing"

Expand Down Expand Up @@ -41,7 +40,7 @@ func TestHasType(t *testing.T) {
test("no match", errorsx.HasType[barError], errFoo, false)
test("match (exact)", errorsx.HasType[fooError], errFoo, true)
test("match (wrapped)", errorsx.HasType[fooError], wrap(errFoo), true)
test("match (interface)", errorsx.HasType[interface{ Timeout() bool }], errTimeout, true)
test("match (interface)", errorsx.HasType[interface{ Error() string }], errFoo, true)
}

func TestSplit(t *testing.T) {
Expand All @@ -61,23 +60,6 @@ func TestSplit(t *testing.T) {
test("joined errors (fmt.Errorf)", fmt.Errorf("%w; %w", errFoo, errBar), []error{errFoo, errBar})
}

func TestIsTimeout(t *testing.T) {
test := func(name string, fn func(error) bool, err error, want bool) {
t.Helper()
t.Run(name, func(t *testing.T) {
t.Helper()
if got := fn(err); got != want {
t.Errorf("got %t; want %t", got, want)
}
})
}

test("os.IsTimeout", os.IsTimeout, errTimeout, true)
test("os.IsTimeout (wrapped)", os.IsTimeout, wrap(errTimeout), false)
test("errorsx.IsTimeout", errorsx.IsTimeout, errTimeout, true)
test("errorsx.IsTimeout (wrapped)", errorsx.IsTimeout, wrap(errTimeout), true)
}

func TestClose(t *testing.T) {
test := func(name string, mainErr, closeErr error, wantErrs []error) {
t.Helper()
Expand All @@ -103,9 +85,8 @@ func TestClose(t *testing.T) {
}

var (
errFoo fooError
errBar barError
errTimeout timeoutError
errFoo fooError
errBar barError
)

type fooError struct{}
Expand All @@ -116,12 +97,7 @@ type barError struct{}

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

type timeoutError struct{}

func (timeoutError) Error() string { return "timeout" }
func (timeoutError) Timeout() bool { return true }

func wrap(err error) error { return fmt.Errorf("wrapped: %w", err) }
func wrap(err error) error { return fmt.Errorf("%w", err) }

type errCloser struct{ err error }

Expand Down
16 changes: 6 additions & 10 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package errorsx_test

import (
"fmt"
"os"

"go-simpler.org/errorsx"
Expand All @@ -10,30 +11,25 @@ var err error

func ExampleIsAny() {
if errorsx.IsAny(err, os.ErrNotExist, os.ErrPermission) {
// handle error
fmt.Println(err)
}
}

func ExampleHasType() {
if errorsx.HasType[*os.PathError](err) {
// handle error
fmt.Println(err)
}
}

func ExampleSplit() {
if errs := errorsx.Split(err); errs != nil {
// handle errors
}
}

func ExampleIsTimeout() {
if errorsx.IsTimeout(err) {
// handle timeout
fmt.Println(errs)
}
}

//nolint:errcheck // this is just an example.
func ExampleClose() {
_ = func() (err error) {
func() (err error) {
f, err := os.Open("file.txt")
if err != nil {
return err
Expand Down
Loading