Skip to content

Commit da0c610

Browse files
authored
feat: add JoinErrors (#5)
1 parent 178858a commit da0c610

File tree

4 files changed

+38
-27
lines changed

4 files changed

+38
-27
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ go get go-simpler.org/check
1616
## 📋 Usage
1717

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

2121
```go
22-
errs := check.
22+
err := check.
2323
That(user.Name != "", errEmptyName).
2424
Thatf(user.Age >= 18, "%d y.o. is too young", user.Age).
2525
Thatf(isEmail(user.Email), "%s is invalid email", user.Email).
26-
AllErrors() // OR FirstError() to check only the first error.
26+
JoinErrors() // or FirstError() / AllErrors().
2727
```

check.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// Package check provides convenience helpers to perform validations of any kind.
22
//
3-
// Use That/Thatf to write conditions to check, multiple calls can be chained.
4-
// The last call in the chain must be either FirstError or AllErrors.
3+
// Use [That]/[Thatf] to write conditions to check, multiple calls can be chained.
4+
// The last call in the chain must be FirstError, AllErrors, or JoinErrors.
55
package check
66

7-
import "fmt"
7+
import (
8+
"errors"
9+
"fmt"
10+
)
811

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

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

41-
// FirstError returns the error of the first failed condition.
44+
// FirstError returns the first recorded error.
4245
func (s *State) FirstError() error {
4346
if len(s.errs) > 0 {
4447
return s.errs[0]
4548
}
4649
return nil
4750
}
4851

49-
// AllErrors returns the errors of all failed conditions.
52+
// AllErrors returns all the recorded errors.
5053
func (s *State) AllErrors() []error { return s.errs }
54+
55+
// JoinErrors returns all the recorded errors joined via [errors.Join].
56+
func (s *State) JoinErrors() error { return errors.Join(s.errs...) }

check_test.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,43 @@ import (
88
)
99

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

15-
err := check.
16-
That(1 == 2, err12).
17-
That(3 == 4, err34).
18-
FirstError()
14+
state := check.
15+
That(1 == 2, err12).
16+
That(3 == 4, err34)
17+
18+
t.Run("first error", func(t *testing.T) {
19+
err := state.FirstError()
1920

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

2526
t.Run("all errors", func(t *testing.T) {
26-
errs := check.
27-
Thatf("foo" == "baz", "foo and bar are not equal").
28-
Thatf(true == false, "true and false are not equal").
29-
AllErrors()
27+
errs := state.AllErrors()
3028

3129
if len(errs) != 2 {
3230
t.Fatalf("want 2 errors")
3331
}
34-
3532
if errs[0] == nil || errs[1] == nil {
3633
t.Errorf("want all errors to be non-nil")
3734
}
3835
})
3936

37+
t.Run("join errors", func(t *testing.T) {
38+
err := state.JoinErrors()
39+
40+
if !errors.Is(err, err12) {
41+
t.Errorf("got %v; want %v", err, err12)
42+
}
43+
if !errors.Is(err, err34) {
44+
t.Errorf("got %v; want %v", err, err34)
45+
}
46+
})
47+
4048
t.Run("panic on nil error", func(t *testing.T) {
4149
defer func() {
4250
if r := recover(); r == nil {

example_test.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,13 @@ func isEmail(string) bool { return false }
2222
var errEmptyName = errors.New("name must not be empty")
2323

2424
func Example() {
25-
errs := check.
25+
err := check.
2626
That(user.Name != "", errEmptyName).
2727
Thatf(user.Age >= 18, "%d y.o. is too young", user.Age).
2828
Thatf(isEmail(user.Email), "%s is invalid email", user.Email).
29-
AllErrors() // OR FirstError() to check only the first error.
30-
31-
for _, err := range errs {
32-
fmt.Println(err)
33-
}
29+
JoinErrors() // or FirstError() / AllErrors().
3430

31+
fmt.Println(err)
3532
// Output:
3633
// name must not be empty
3734
// 10 y.o. is too young

0 commit comments

Comments
 (0)