Skip to content

Commit

Permalink
feat(errmsg): support errors wrapped through errors.Join (#21)
Browse files Browse the repository at this point in the history
Because

- When joining several errors with end-user messages through
`errors.Join`,
  `errmsg.Message` returned only the first end-user message.

This commit

- Supports multiple end-user messages joined with `errors.Join`.
  • Loading branch information
jvallesm authored Oct 3, 2024
1 parent fd24391 commit 5ced228
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 15 deletions.
23 changes: 19 additions & 4 deletions errmsg/errmsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package errmsg
import (
"errors"
"fmt"
"strings"
)

// endUserError is an error that holds an end-user message.
Expand All @@ -18,7 +19,7 @@ func (e *endUserError) Error() string { return e.cause.Error() }
func (e *endUserError) Unwrap() error { return e.cause }

// As implements the required function to ensure errors.As can properly match
// endUserEror targets.
// endUserError targets.
func (e *endUserError) As(target any) bool {
if tgt, ok := target.(**endUserError); ok {
*tgt = e
Expand All @@ -44,9 +45,23 @@ func AddMessage(err error, msg string) error {
// Message extracts an end-user message from the error.
func Message(err error) string {
for err != nil {
eu := new(endUserError)
if errors.As(err, &eu) && eu.message != "" {
return eu.message
if endUserErr, ok := err.(*endUserError); ok {
return endUserErr.message
}

// If the error was generated through errors.Join, Unwrap returns an
// array of errors, several of which might contain a message.
if joinedErr, ok := err.(interface{ Unwrap() []error }); ok {
unwrappedErrs := joinedErr.Unwrap()
msgs := make([]string, 0, len(unwrappedErrs))
for _, uwe := range unwrappedErrs {
msg := Message(uwe)
if msg != "" {
msgs = append(msgs, Message(uwe))
}
}

return strings.Join(msgs, " ")
}

err = errors.Unwrap(err)
Expand Down
23 changes: 12 additions & 11 deletions errmsg/errmsg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,18 @@ func TestAddAndExtractMessage(t *testing.T) {
},
{
name: "multi-message error",
wantMsg: "An error happened. Something went wrong.",
wantErr: "bang: boom",
err: AddMessage(
// handle error coming from downstream
fmt.Errorf("bang: %w",
// downstream error also contains message
AddMessage(errors.New("boom"), "Something went wrong."),
),
// add message to downstream error
"An error happened.",
),
wantMsg: "Please check your input. Something went wrong in condition 1. Condition 2 failed.",
wantErr: "checking conditions: evaluating condition 1: boom\nbang",
err: func() error {
cond2Err := AddMessage(fmt.Errorf("bang"), "Condition 2 failed.")
cond1Err := fmt.Errorf(
"evaluating condition 1: %w",
AddMessage(fmt.Errorf("boom"), "Something went wrong in condition 1."),
)

jointErr := fmt.Errorf("checking conditions: %w", errors.Join(cond1Err, cond2Err))
return AddMessage(jointErr, "Please check your input.")
}(),
},
}

Expand Down

0 comments on commit 5ced228

Please sign in to comment.