From 64de635626041fcfb6b9ad8e8ff79e5870701412 Mon Sep 17 00:00:00 2001 From: Albin Kerouanton Date: Thu, 10 Aug 2023 17:49:00 +0200 Subject: [PATCH] Add a temporary drop-in replacement for errors.Join As we have a hard time figuring out what moby/moby#46099 should look like, this drop-in replacement will solve the initial formatting problem we have. It's made internal such that we can remove it whenever we want and unlike moby/moby#46099 doesn't require thoughtful API changes. Signed-off-by: Albin Kerouanton Signed-off-by: Sebastiaan van Stijn --- internal/multierror/multierror.go | 46 ++++++++++++++++++++++++++ internal/multierror/multierror_test.go | 25 ++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 internal/multierror/multierror.go create mode 100644 internal/multierror/multierror_test.go diff --git a/internal/multierror/multierror.go b/internal/multierror/multierror.go new file mode 100644 index 0000000000..cf4d6a5957 --- /dev/null +++ b/internal/multierror/multierror.go @@ -0,0 +1,46 @@ +package multierror + +import ( + "strings" +) + +// Join is a drop-in replacement for errors.Join with better formatting. +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + if len(e.errs) == 1 { + return strings.TrimSpace(e.errs[0].Error()) + } + stringErrs := make([]string, 0, len(e.errs)) + for _, subErr := range e.errs { + stringErrs = append(stringErrs, strings.Replace(subErr.Error(), "\n", "\n\t", -1)) + } + return "* " + strings.Join(stringErrs, "\n* ") +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/internal/multierror/multierror_test.go b/internal/multierror/multierror_test.go new file mode 100644 index 0000000000..2d46240197 --- /dev/null +++ b/internal/multierror/multierror_test.go @@ -0,0 +1,25 @@ +package multierror + +import ( + "errors" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func TestErrorJoin(t *testing.T) { + t.Run("single", func(t *testing.T) { + err := Join(fmt.Errorf("invalid config: %w", Join(errors.New("foo")))) + const expected = `invalid config: foo` + assert.Equal(t, err.Error(), expected) + }) + t.Run("multiple", func(t *testing.T) { + err := Join(errors.New("foobar"), fmt.Errorf("invalid config: \n%w", Join(errors.New("foo"), errors.New("bar")))) + const expected = `* foobar +* invalid config: + * foo + * bar` + assert.Equal(t, err.Error(), expected) + }) +}