Skip to content

Commit

Permalink
feat: add JoinErrors (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmzane authored Oct 11, 2023
1 parent 178858a commit da0c610
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 27 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ go get go-simpler.org/check
## 📋 Usage

Use `That`/`Thatf` to write conditions to check, multiple calls can be chained.
The last call in the chain must be either `FirstError` or `AllErrors`.
The last call in the chain must be `FirstError`, `AllErrors`, or `JoinErrors`.

```go
errs := check.
err := check.
That(user.Name != "", errEmptyName).
Thatf(user.Age >= 18, "%d y.o. is too young", user.Age).
Thatf(isEmail(user.Email), "%s is invalid email", user.Email).
AllErrors() // OR FirstError() to check only the first error.
JoinErrors() // or FirstError() / AllErrors().
```
18 changes: 12 additions & 6 deletions check.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Package check provides convenience helpers to perform validations of any kind.
//
// Use That/Thatf to write conditions to check, multiple calls can be chained.
// The last call in the chain must be either FirstError or AllErrors.
// Use [That]/[Thatf] to write conditions to check, multiple calls can be chained.
// The last call in the chain must be FirstError, AllErrors, or JoinErrors.
package check

import "fmt"
import (
"errors"
"fmt"
)

// That checks whether the condition is true, and if not, records the error.
func That(cond bool, err error) *State {
Expand All @@ -16,7 +19,7 @@ func Thatf(cond bool, format string, args ...any) *State {
return new(State).Thatf(cond, format, args...)
}

// State holds the errors of the failed conditions.
// State holds the recorded errors.
// It is exported only for the purpose of documentation.
type State struct {
errs []error
Expand All @@ -38,13 +41,16 @@ func (s *State) Thatf(cond bool, format string, args ...any) *State {
return s.That(cond, fmt.Errorf(format, args...))
}

// FirstError returns the error of the first failed condition.
// FirstError returns the first recorded error.
func (s *State) FirstError() error {
if len(s.errs) > 0 {
return s.errs[0]
}
return nil
}

// AllErrors returns the errors of all failed conditions.
// AllErrors returns all the recorded errors.
func (s *State) AllErrors() []error { return s.errs }

// JoinErrors returns all the recorded errors joined via [errors.Join].
func (s *State) JoinErrors() error { return errors.Join(s.errs...) }
32 changes: 20 additions & 12 deletions check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,43 @@ import (
)

func TestCheck(t *testing.T) {
t.Run("first error", func(t *testing.T) {
err12 := errors.New("1 and 2 are not equal")
err34 := errors.New("3 and 4 are not equal")
err12 := errors.New("1 and 2 are not equal")
err34 := errors.New("3 and 4 are not equal")

err := check.
That(1 == 2, err12).
That(3 == 4, err34).
FirstError()
state := check.
That(1 == 2, err12).
That(3 == 4, err34)

t.Run("first error", func(t *testing.T) {
err := state.FirstError()

if !errors.Is(err, err12) {
t.Errorf("got %v; want %v", err, err12)
}
})

t.Run("all errors", func(t *testing.T) {
errs := check.
Thatf("foo" == "baz", "foo and bar are not equal").
Thatf(true == false, "true and false are not equal").
AllErrors()
errs := state.AllErrors()

if len(errs) != 2 {
t.Fatalf("want 2 errors")
}

if errs[0] == nil || errs[1] == nil {
t.Errorf("want all errors to be non-nil")
}
})

t.Run("join errors", func(t *testing.T) {
err := state.JoinErrors()

if !errors.Is(err, err12) {
t.Errorf("got %v; want %v", err, err12)
}
if !errors.Is(err, err34) {
t.Errorf("got %v; want %v", err, err34)
}
})

t.Run("panic on nil error", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
Expand Down
9 changes: 3 additions & 6 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ func isEmail(string) bool { return false }
var errEmptyName = errors.New("name must not be empty")

func Example() {
errs := check.
err := check.
That(user.Name != "", errEmptyName).
Thatf(user.Age >= 18, "%d y.o. is too young", user.Age).
Thatf(isEmail(user.Email), "%s is invalid email", user.Email).
AllErrors() // OR FirstError() to check only the first error.

for _, err := range errs {
fmt.Println(err)
}
JoinErrors() // or FirstError() / AllErrors().

fmt.Println(err)
// Output:
// name must not be empty
// 10 y.o. is too young
Expand Down

0 comments on commit da0c610

Please sign in to comment.