github.com/cockroachdb/errors@v1.11.1/errbase/decode.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied. See the License for the specific language governing
    13  // permissions and limitations under the License.
    14  
    15  package errbase
    16  
    17  import (
    18  	"context"
    19  
    20  	"github.com/cockroachdb/errors/errorspb"
    21  	"github.com/gogo/protobuf/proto"
    22  	"github.com/gogo/protobuf/types"
    23  )
    24  
    25  // DecodeError decodes an error.
    26  //
    27  // Can only be called if the EncodedError is set (see IsSet()).
    28  func DecodeError(ctx context.Context, enc EncodedError) error {
    29  	if w := enc.GetWrapper(); w != nil {
    30  		return decodeWrapper(ctx, w)
    31  	}
    32  	return decodeLeaf(ctx, enc.GetLeaf())
    33  }
    34  
    35  func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
    36  	// In case there is a detailed payload, decode it.
    37  	var payload proto.Message
    38  	if enc.Details.FullDetails != nil {
    39  		var d types.DynamicAny
    40  		err := types.UnmarshalAny(enc.Details.FullDetails, &d)
    41  		if err != nil {
    42  			// It's OK if we can't decode. We'll use
    43  			// the opaque type below.
    44  			warningFn(ctx, "error while unmarshalling error: %+v", err)
    45  		} else {
    46  			payload = d.Message
    47  		}
    48  	}
    49  
    50  	// Do we have a leaf decoder for this type?
    51  	typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
    52  	if decoder, ok := leafDecoders[typeKey]; ok {
    53  		// Yes, use it.
    54  		genErr := decoder(ctx, enc.Message, enc.Details.ReportablePayload, payload)
    55  		if genErr != nil {
    56  			// Decoding succeeded. Use this.
    57  			return genErr
    58  		}
    59  		// Decoding failed, we'll drop through to opaqueLeaf{} below.
    60  	} else if decoder, ok := multiCauseDecoders[typeKey]; ok {
    61  		causes := make([]error, len(enc.MultierrorCauses))
    62  		for i, e := range enc.MultierrorCauses {
    63  			causes[i] = DecodeError(ctx, *e)
    64  		}
    65  		genErr := decoder(ctx, causes, enc.Message, enc.Details.ReportablePayload, payload)
    66  		if genErr != nil {
    67  			return genErr
    68  		}
    69  	} else {
    70  		// Shortcut for non-registered proto-encodable error types:
    71  		// if it already implements `error`, it's good to go.
    72  		if e, ok := payload.(error); ok {
    73  			// yes: we're done!
    74  			return e
    75  		}
    76  	}
    77  
    78  	if len(enc.MultierrorCauses) > 0 {
    79  		causes := make([]error, len(enc.MultierrorCauses))
    80  		for i, e := range enc.MultierrorCauses {
    81  			causes[i] = DecodeError(ctx, *e)
    82  		}
    83  		leaf := &opaqueLeafCauses{
    84  			causes: causes,
    85  		}
    86  		leaf.msg = enc.Message
    87  		leaf.details = enc.Details
    88  		return leaf
    89  	}
    90  
    91  	// No decoder and no error type: we'll keep what we received and
    92  	// make it ready to re-encode exactly (if the error leaves over the
    93  	// network again).
    94  	return &opaqueLeaf{
    95  		msg:     enc.Message,
    96  		details: enc.Details,
    97  	}
    98  }
    99  
   100  func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
   101  	// First decode the cause.
   102  	cause := DecodeError(ctx, enc.Cause)
   103  
   104  	// In case there is a detailed payload, decode it.
   105  	var payload proto.Message
   106  	if enc.Details.FullDetails != nil {
   107  		var d types.DynamicAny
   108  		err := types.UnmarshalAny(enc.Details.FullDetails, &d)
   109  		if err != nil {
   110  			// It's OK if we can't decode. We'll use
   111  			// the opaque type below.
   112  			warningFn(ctx, "error while unmarshalling wrapper error: %+v", err)
   113  		} else {
   114  			payload = d.Message
   115  		}
   116  	}
   117  
   118  	// Do we have a wrapper decoder for this?
   119  	typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
   120  	if decoder, ok := decoders[typeKey]; ok {
   121  		// Yes, use it.
   122  		genErr := decoder(ctx, cause, enc.Message, enc.Details.ReportablePayload, payload)
   123  		if genErr != nil {
   124  			// Decoding succeeded. Use this.
   125  			return genErr
   126  		}
   127  		// Decoding failed, we'll drop through to opaqueWrapper{} below.
   128  	}
   129  
   130  	// Otherwise, preserve all details about the original object.
   131  	return &opaqueWrapper{
   132  		cause:       cause,
   133  		prefix:      enc.Message,
   134  		details:     enc.Details,
   135  		messageType: MessageType(enc.MessageType),
   136  	}
   137  }
   138  
   139  // RegisterLeafDecoder can be used to register new leaf error types to
   140  // the library. Registered types will be decoded using their own
   141  // Go type when an error is decoded. Wrappers that have not been
   142  // registered will be decoded using the opaqueLeaf type.
   143  //
   144  // Note: if the error type has been migrated from a previous location
   145  // or a different type, ensure that RegisterTypeMigration() was called
   146  // prior to RegisterLeafDecoder().
   147  func RegisterLeafDecoder(theType TypeKey, decoder LeafDecoder) {
   148  	if decoder == nil {
   149  		delete(leafDecoders, theType)
   150  	} else {
   151  		leafDecoders[theType] = decoder
   152  	}
   153  }
   154  
   155  // LeafDecoder is to be provided (via RegisterLeafDecoder above)
   156  // by additional wrapper types not yet known to this library.
   157  // A nil return indicates that decoding was not successful.
   158  type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
   159  
   160  // registry for RegisterLeafDecoder.
   161  var leafDecoders = map[TypeKey]LeafDecoder{}
   162  
   163  // RegisterWrapperDecoder can be used to register new wrapper types to
   164  // the library. Registered wrappers will be decoded using their own
   165  // Go type when an error is decoded. Wrappers that have not been
   166  // registered will be decoded using the opaqueWrapper type.
   167  //
   168  // Note: if the error type has been migrated from a previous location
   169  // or a different type, ensure that RegisterTypeMigration() was called
   170  // prior to RegisterWrapperDecoder().
   171  func RegisterWrapperDecoder(theType TypeKey, decoder WrapperDecoder) {
   172  	if decoder == nil {
   173  		delete(decoders, theType)
   174  	} else {
   175  		decoders[theType] = decoder
   176  	}
   177  }
   178  
   179  // WrapperDecoder is to be provided (via RegisterWrapperDecoder above)
   180  // by additional wrapper types not yet known to this library.
   181  // A nil return indicates that decoding was not successful.
   182  type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
   183  
   184  // registry for RegisterWrapperType.
   185  var decoders = map[TypeKey]WrapperDecoder{}
   186  
   187  // MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
   188  // above) by additional multi-cause wrapper types not yet known by the
   189  // library. A nil return indicates that decoding was not successful.
   190  type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
   191  
   192  // registry for RegisterMultiCauseDecoder.
   193  var multiCauseDecoders = map[TypeKey]MultiCauseDecoder{}
   194  
   195  // RegisterMultiCauseDecoder can be used to register new multi-cause
   196  // wrapper types to the library. Registered wrappers will be decoded
   197  // using their own Go type when an error is decoded. Multi-cause
   198  // wrappers that have not been registered will be decoded using the
   199  // opaqueWrapper type.
   200  func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
   201  	if decoder == nil {
   202  		delete(multiCauseDecoders, theType)
   203  	} else {
   204  		multiCauseDecoders[theType] = decoder
   205  	}
   206  }