diff --git a/errmsg/errmsg.go b/errmsg/errmsg.go index 605128d..cd0036e 100644 --- a/errmsg/errmsg.go +++ b/errmsg/errmsg.go @@ -3,6 +3,7 @@ package errmsg import ( "errors" "fmt" + "strings" ) // endUserError is an error that holds an end-user message. @@ -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 @@ -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) diff --git a/errmsg/errmsg_test.go b/errmsg/errmsg_test.go index 432c4f6..2043df1 100644 --- a/errmsg/errmsg_test.go +++ b/errmsg/errmsg_test.go @@ -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.") + }(), }, }