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 }