// Copyright 2019 The Cockroach Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // implied. See the License for the specific language governing // permissions and limitations under the License. package errbase import ( "context" "github.com/cockroachdb/errors/errorspb" "github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/types" ) // DecodeError decodes an error. func DecodeError(ctx context.Context, enc EncodedError) error { if w := enc.GetWrapper(); w != nil { return decodeWrapper(ctx, w) } return decodeLeaf(ctx, enc.GetLeaf()) } func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error { // In case there is a detailed payload, decode it. var payload proto.Message if enc.Details.FullDetails != nil { var d types.DynamicAny err := types.UnmarshalAny(enc.Details.FullDetails, &d) if err != nil { // It's OK if we can't decode. We'll use // the opaque type below. warningFn(ctx, "error while unmarshalling error: %+v", err) } else { payload = d.Message } } // Do we have a leaf decoder for this type? typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName) if decoder, ok := leafDecoders[typeKey]; ok { // Yes, use it. genErr := decoder(ctx, enc.Message, enc.Details.ReportablePayload, payload) if genErr != nil { // Decoding succeeded. Use this. return genErr } // Decoding failed, we'll drop through to opaqueLeaf{} below. } else { // Shortcut for non-registered proto-encodable error types: // if it already implements `error`, it's good to go. if e, ok := payload.(error); ok { // yes: we're done! return e } } // 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). return &opaqueLeaf{ msg: enc.Message, details: enc.Details, } } func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error { // First decode the cause. cause := DecodeError(ctx, enc.Cause) // In case there is a detailed payload, decode it. var payload proto.Message if enc.Details.FullDetails != nil { var d types.DynamicAny err := types.UnmarshalAny(enc.Details.FullDetails, &d) if err != nil { // It's OK if we can't decode. We'll use // the opaque type below. warningFn(ctx, "error while unmarshalling wrapper error: %+v", err) } else { payload = d.Message } } // Do we have a wrapper decoder for this? typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName) if decoder, ok := decoders[typeKey]; ok { // Yes, use it. genErr := decoder(ctx, cause, enc.MessagePrefix, enc.Details.ReportablePayload, payload) if genErr != nil { // Decoding succeeded. Use this. return genErr } // Decoding failed, we'll drop through to opaqueWrapper{} below. } // Otherwise, preserve all details about the original object. return &opaqueWrapper{ cause: cause, prefix: enc.MessagePrefix, details: enc.Details, } } // RegisterLeafDecoder can be used to register new leaf error types to // the library. Registered types will be decoded using their own // Go type when an error is decoded. Wrappers that have not been // registered will be decoded using the opaqueLeaf type. // // Note: if the error type has been migrated from a previous location // or a different type, ensure that RegisterTypeMigration() was called // prior to RegisterLeafDecoder(). func RegisterLeafDecoder(theType TypeKey, decoder LeafDecoder) { if decoder == nil { delete(leafDecoders, theType) } else { leafDecoders[theType] = decoder } } // LeafDecoder is to be provided (via RegisterLeafDecoder above) // by additional wrapper types not yet known to this library. // A nil return indicates that decoding was not successful. type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error // registry for RegisterLeafDecoder. var leafDecoders = map[TypeKey]LeafDecoder{} // RegisterWrapperDecoder can be used to register new wrapper types to // the library. Registered wrappers will be decoded using their own // Go type when an error is decoded. Wrappers that have not been // registered will be decoded using the opaqueWrapper type. // // Note: if the error type has been migrated from a previous location // or a different type, ensure that RegisterTypeMigration() was called // prior to RegisterWrapperDecoder(). func RegisterWrapperDecoder(theType TypeKey, decoder WrapperDecoder) { if decoder == nil { delete(decoders, theType) } else { decoders[theType] = decoder } } // WrapperDecoder is to be provided (via RegisterWrapperDecoder above) // by additional wrapper types not yet known to this library. // A nil return indicates that decoding was not successful. type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error // registry for RegisterWrapperType. var decoders = map[TypeKey]WrapperDecoder{}