Last active
March 31, 2020 14:39
-
-
Save felixge/3ca60f00530146b5b5d515e4c8604bdc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package multiwrap implements support for wrapping multiple errors into a | |
// single error value that supports the Go 1.13 Unwrap(), Is() and As() | |
// interface. It's similar to go-multierror [1]. | |
// | |
// This is a proof of concept and I'm looking for feedback! Maybe somebody has | |
// already done a nicer version of this idea? | |
// | |
// [1] https://godoc.org/github.com/hashicorp/go-multierror | |
package multiwrap | |
import ( | |
"errors" | |
"strings" | |
) | |
func Multiwrap(errs ...error) error { | |
var top *multiwrap | |
var current *multiwrap | |
for _, err := range errs { | |
if current == nil { | |
top = &multiwrap{error: err} | |
current = top | |
} else { | |
current.next = &multiwrap{error: err} | |
current = current.next | |
} | |
} | |
if top == nil { | |
return nil | |
} | |
return top | |
} | |
type multiwrap struct { | |
error | |
next *multiwrap | |
} | |
func (m *multiwrap) Error() string { | |
var msgs []string | |
for m != nil { | |
msgs = append(msgs, m.error.Error()) | |
m = m.next | |
} | |
return strings.Join(msgs, ": ") | |
} | |
func (m *multiwrap) Unwrap() error { | |
if m == nil { | |
return nil | |
} | |
return m.next | |
} | |
func (m *multiwrap) Is(target error) bool { | |
if m == nil { | |
return false | |
} | |
return errors.Is(m.error, target) | |
} | |
func (m *multiwrap) As(target interface{}) bool { | |
if m == nil { | |
return false | |
} | |
return errors.As(m.error, target) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package multiwrap | |
import ( | |
"errors" | |
"testing" | |
) | |
func TestMultiwrap(t *testing.T) { | |
tests := []struct { | |
Wrap []error | |
Is []error | |
IsNot []error | |
Msg string | |
Nil bool | |
}{ | |
{ | |
Wrap: []error{}, | |
Is: []error{}, | |
IsNot: []error{A{}, B{}, C{}}, | |
Nil: true, | |
}, | |
{ | |
Wrap: []error{A{}}, | |
Is: []error{A{}}, | |
IsNot: []error{B{}, C{}}, | |
Msg: "A", | |
}, | |
{ | |
Wrap: []error{A{}, B{}}, | |
Is: []error{A{}, B{}}, | |
IsNot: []error{C{}}, | |
Msg: "A: B", | |
}, | |
{ | |
Wrap: []error{B{}, A{}}, | |
Is: []error{A{}, B{}}, | |
IsNot: []error{C{}}, | |
Msg: "B: A", | |
}, | |
{ | |
Wrap: []error{A{}, B{}, C{}}, | |
Is: []error{A{}, B{}, C{}}, | |
IsNot: []error{}, | |
Msg: "A: B: C", | |
}, | |
} | |
for i, test := range tests { | |
err := Multiwrap(test.Wrap...) | |
if test.Nil { | |
if err != nil { | |
t.Errorf("test=%d want nil, got=%#v", i, err) | |
} | |
continue | |
} | |
for _, want := range test.Is { | |
if !errors.Is(err, want) { | |
t.Errorf("test=%d want %T", i, want) | |
} | |
} | |
for _, want := range test.IsNot { | |
if errors.Is(err, want) { | |
t.Errorf("test=%d want not %T", i, want) | |
} | |
} | |
if err.Error() != test.Msg { | |
t.Errorf("test=%d got=%q want=%q", i, err.Error(), test.Msg) | |
} | |
} | |
} | |
type A struct{} | |
func (a A) Error() string { return "A" } | |
type B struct{} | |
func (a B) Error() string { return "B" } | |
type C struct{} | |
func (a C) Error() string { return "C" } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment