Skip to content

Commit

Permalink
Merge pull request #121 from cockroachdb/go-1.20-upgrade
Browse files Browse the repository at this point in the history
Upgrade to go 1.20 and add multi-cause error support
  • Loading branch information
dhartunian authored Aug 24, 2023
2 parents 5197958 + 3a3abac commit ca59e56
Show file tree
Hide file tree
Showing 37 changed files with 17,764 additions and 2,282 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ jobs:
strategy:
matrix:
go:
- "1.17"
- "1.18"
- "1.19"
- "1.20"
- "1.21"
steps:
- uses: actions/checkout@v2

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,8 @@ Example use:
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |

| `Join` | `JoinWithDepth` (see below) |
| `JoinWithDepth` | multi-cause wrapper + `WithStackDepth` |
## API (not constructing error objects)

The following is a summary of the non-constructor API functions, grouped by category.
Expand Down Expand Up @@ -574,11 +575,15 @@ func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder)
func RegisterWrapperEncoderWithMessageOverride (typeName TypeKey, encoder WrapperEncoderWithMessageOverride)
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder)
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder)
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
type WrapperEncoderWithMessageOverride = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message, overrideError bool)
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
// Registering package renames for custom error types.
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)
Expand Down
26 changes: 26 additions & 0 deletions errbase/adapters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,33 @@ func TestAdaptGoSingleWrapErr(t *testing.T) {
tt := testutils.T{T: t}
// The library preserves the cause. It's not possible to preserve the fmt
// string.
tt.CheckEqual(newErr.Error(), origErr.Error())
tt.CheckContains(newErr.Error(), "hello")
}

func TestAdaptBaseGoJoinErr(t *testing.T) {
origErr := goErr.Join(goErr.New("hello"), goErr.New("world"))
t.Logf("start err: %# v", pretty.Formatter(origErr))

newErr := network(t, origErr)

tt := testutils.T{T: t}
// The library preserves the error message.
tt.CheckEqual(newErr.Error(), origErr.Error())

}

func TestAdaptGoMultiWrapErr(t *testing.T) {
origErr := fmt.Errorf("an error %w and also %w", goErr.New("hello"), goErr.New("world"))
t.Logf("start err: %# v", pretty.Formatter(origErr))

newErr := network(t, origErr)

tt := testutils.T{T: t}
// The library preserves the causes. It's not possible to preserve the fmt string.
tt.CheckEqual(newErr.Error(), origErr.Error())
tt.CheckContains(newErr.Error(), "hello")
tt.CheckContains(newErr.Error(), "world")
}

func TestAdaptPkgWithMessage(t *testing.T) {
Expand Down
43 changes: 43 additions & 0 deletions errbase/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
return genErr
}
// Decoding failed, we'll drop through to opaqueLeaf{} below.
} else if decoder, ok := multiCauseDecoders[typeKey]; ok {
causes := make([]error, len(enc.MultierrorCauses))
for i, e := range enc.MultierrorCauses {
causes[i] = DecodeError(ctx, *e)
}
genErr := decoder(ctx, causes, enc.Message, enc.Details.ReportablePayload, payload)
if genErr != nil {
return genErr
}
} else {
// Shortcut for non-registered proto-encodable error types:
// if it already implements `error`, it's good to go.
Expand All @@ -66,6 +75,19 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
}
}

if len(enc.MultierrorCauses) > 0 {
causes := make([]error, len(enc.MultierrorCauses))
for i, e := range enc.MultierrorCauses {
causes[i] = DecodeError(ctx, *e)
}
leaf := &opaqueLeafCauses{
causes: causes,
}
leaf.msg = enc.Message
leaf.details = enc.Details
return leaf
}

// No decoder and no error type: we'll keep what we received and
// make it ready to re-encode exactly (if the error leaves over the
// network again).
Expand Down Expand Up @@ -161,3 +183,24 @@ type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, s

// registry for RegisterWrapperType.
var decoders = map[TypeKey]WrapperDecoder{}

// MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
// above) by additional multi-cause wrapper types not yet known by the
// library. A nil return indicates that decoding was not successful.
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error

// registry for RegisterMultiCauseDecoder.
var multiCauseDecoders = map[TypeKey]MultiCauseDecoder{}

// RegisterMultiCauseDecoder can be used to register new multi-cause
// wrapper types to the library. Registered wrappers will be decoded
// using their own Go type when an error is decoded. Multi-cause
// wrappers that have not been registered will be decoded using the
// opaqueWrapper type.
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
if decoder == nil {
delete(multiCauseDecoders, theType)
} else {
multiCauseDecoders[theType] = decoder
}
}
52 changes: 46 additions & 6 deletions errbase/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,24 @@ func EncodeError(ctx context.Context, err error) EncodedError {
if cause := UnwrapOnce(err); cause != nil {
return encodeWrapper(ctx, err, cause)
}
// Not a causer.
return encodeLeaf(ctx, err)
return encodeLeaf(ctx, err, UnwrapMulti(err))
}

// encodeLeaf encodes a leaf error.
func encodeLeaf(ctx context.Context, err error) EncodedError {
// encodeLeaf encodes a leaf error. This function accepts a `causes`
// argument because we encode multi-cause errors using the Leaf
// protobuf. This was done to enable backwards compatibility when
// introducing this functionality since the Wrapper type already has a
// required single `cause` field.
func encodeLeaf(ctx context.Context, err error, causes []error) EncodedError {
var msg string
var details errorspb.EncodedErrorDetails

if e, ok := err.(*opaqueLeaf); ok {
msg = e.msg
details = e.details
} else if e, ok := err.(*opaqueLeafCauses); ok {
msg = e.msg
details = e.details
} else {
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)

Expand Down Expand Up @@ -74,11 +80,21 @@ func encodeLeaf(ctx context.Context, err error) EncodedError {
details.FullDetails = encodeAsAny(ctx, err, payload)
}

var cs []*EncodedError
if len(causes) > 0 {
cs = make([]*EncodedError, len(causes))
for i, ee := range causes {
ee := EncodeError(ctx, ee)
cs[i] = &ee
}
}

return EncodedError{
Error: &errorspb.EncodedError_Leaf{
Leaf: &errorspb.EncodedErrorLeaf{
Message: msg,
Details: details,
Message: msg,
Details: details,
MultierrorCauses: cs,
},
},
}
Expand Down Expand Up @@ -207,6 +223,8 @@ func getTypeDetails(
switch t := err.(type) {
case *opaqueLeaf:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
case *opaqueLeafCauses:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
case *opaqueWrapper:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
}
Expand Down Expand Up @@ -310,6 +328,28 @@ type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails
// registry for RegisterLeafEncoder.
var leafEncoders = map[TypeKey]LeafEncoder{}

// RegisterMultiCauseEncoder can be used to register new multi-cause
// error types to the library. Registered types will be encoded using
// their own Go type when an error is encoded. Multi-cause wrappers
// that have not been registered will be encoded using the
// opaqueWrapper type.
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder) {
// This implementation is a simple wrapper around `LeafEncoder`
// because we implemented multi-cause error wrapper encoding into a
// `Leaf` instead of a `Wrapper` for smoother backwards
// compatibility support. Exposing this detail to consumers of the
// API is confusing and hence avoided. The causes of the error are
// encoded separately regardless of this encoder's implementation.
RegisterLeafEncoder(theType, encoder)
}

// MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
// above) by additional multi-cause wrapper types not yet known to this
// library. The encoder will automatically extract and encode the
// causes of this error by calling `Unwrap()` and expecting a slice of
// errors.
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)

// RegisterWrapperEncoder can be used to register new wrapper types to
// the library. Registered wrappers will be encoded using their own
// Go type when an error is encoded. Wrappers that have not been
Expand Down
Loading

0 comments on commit ca59e56

Please sign in to comment.