Skip to content
Closed
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ func main() {
}
```

#### Nil-Safe Wrapping

`WrapNil` and `WrapfNil` return `nil` if the wrapped error is `nil`, avoiding the need for an explicit `if err != nil` check before wrapping:

```go
func tryOpen() error {
_, err := os.Open("/i/dont/exist")
return errwrap.WrapfNil("Doesn't exist: {{err}}", err)
}

func doStep() error {
err := step()
return errwrap.WrapNil(errors.New("step failed"), err)
}
```

#### Custom Types

If you're already making custom types that properly wrap errors, then
Expand Down
25 changes: 25 additions & 0 deletions errwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ func Wrap(outer, inner error) error {
}
}

// WrapNil wraps outer around inner if inner is not nil. If inner is nil,
// it returns nil.
func WrapNil(outer, inner error) error {
if inner == nil {
return nil
}

return Wrap(outer, inner)
}

// Wrapf wraps an error with a formatting message. This is similar to using
// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap
// errors, you should replace it with this.
Expand All @@ -61,6 +71,21 @@ func Wrapf(format string, err error) error {
return Wrap(outer, err)
}

// WrapfNil wraps an error with a formatting message if err is not nil.
// If err is nil, it returns nil.
//
// format is the format of the error message. The string '{{err}}' will
// be replaced with the original error message.
//
// Deprecated: Use fmt.Errorf()
func WrapfNil(format string, err error) error {
if err == nil {
return nil
}

return Wrapf(format, err)
}

// Contains checks if the given error contains an error with the
// message msg. If err is not a wrapped error, this will always return
// false unless the error itself happens to match this msg.
Expand Down
50 changes: 50 additions & 0 deletions errwrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,53 @@ func TestWrappedError_IsCompatibleWithErrorsUnwrap(t *testing.T) {
t.Fatal("wrappedError did not unwrap to inner")
}
}

func TestWrapNil(t *testing.T) {
if err := WrapNil(errors.New("outer"), nil); err != nil {
t.Fatalf("expected nil when inner is nil, got: %v", err)
}

if err := WrapNil(nil, nil); err != nil {
t.Fatalf("expected nil when both are nil, got: %v", err)
}

inner := errors.New("inner")
outer := errors.New("outer")
err := WrapNil(outer, inner)
if err == nil {
t.Fatal("expected non-nil error when inner is non-nil")
}
if err.Error() != "outer" {
t.Fatalf("expected error %q, got %q", "outer", err.Error())
}
if !Contains(err, "inner") {
t.Fatal("expected error to contain inner")
}
if !Contains(err, "outer") {
t.Fatal("expected error to contain outer")
}
if actual := errors.Unwrap(err); actual != inner {
t.Fatalf("expected unwrapped error to be inner, got %v", actual)
}
}

func TestWrapfNil(t *testing.T) {
if err := WrapfNil("failed: {{err}}", nil); err != nil {
t.Fatalf("expected nil when err is nil, got: %v", err)
}

inner := errors.New("inner")
err := WrapfNil("failed: {{err}}", inner)
if err == nil {
t.Fatal("expected non-nil error when err is non-nil")
}
if err.Error() != "failed: inner" {
t.Fatalf("expected error %q, got %q", "failed: inner", err.Error())
}
if !Contains(err, "inner") {
t.Fatal("expected error to contain inner")
}
if actual := errors.Unwrap(err); actual != inner {
t.Fatalf("expected unwrapped error to be inner, got %v", actual)
}
}