github.com/cockroachdb/errors@v1.11.1/errbase/adapters.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 goErr "errors" 20 "fmt" 21 "os" 22 23 "github.com/cockroachdb/errors/errorspb" 24 "github.com/gogo/protobuf/proto" 25 pkgErr "github.com/pkg/errors" 26 ) 27 28 // This file provides the library the ability to encode/decode 29 // standard error types. 30 31 // errors.errorString from base Go does not need an encoding 32 // function, because the base encoding logic in EncodeLeaf() is 33 // able to extract everything about it. 34 35 // we can then decode it exactly. 36 func decodeErrorString(_ context.Context, msg string, _ []string, _ proto.Message) error { 37 return goErr.New(msg) 38 } 39 40 // context.DeadlineExceeded uses a custom type. 41 func decodeDeadlineExceeded(_ context.Context, _ string, _ []string, _ proto.Message) error { 42 return context.DeadlineExceeded 43 } 44 45 // errors.fundamental from github.com/pkg/errors cannot be encoded 46 // exactly because it includes a non-serializable stack trace 47 // object. In order to work with it, we encode it by dumping 48 // the stack trace in a safe reporting detail field, and decode 49 // it as an opaqueLeaf instance in this package. 50 51 func encodePkgFundamental( 52 _ context.Context, err error, 53 ) (msg string, safe []string, _ proto.Message) { 54 msg = err.Error() 55 iErr := err.(interface{ StackTrace() pkgErr.StackTrace }) 56 safeDetails := []string{fmt.Sprintf("%+v", iErr.StackTrace())} 57 return msg, safeDetails, nil 58 } 59 60 // errors.withMessage from github.com/pkg/errors can be encoded 61 // exactly because it just has a message prefix. The base encoding 62 // logic in EncodeWrapper() is able to extract everything from it. 63 64 // we can then decode it exactly. 65 func decodeWithMessage( 66 _ context.Context, cause error, msgPrefix string, _ []string, _ proto.Message, 67 ) error { 68 return pkgErr.WithMessage(cause, msgPrefix) 69 } 70 71 // errors.withStack from github.com/pkg/errors cannot be encoded 72 // exactly because it includes a non-serializable stack trace 73 // object. In order to work with it, we encode it by dumping 74 // the stack trace in a safe reporting detail field, and decode 75 // it as an opaqueWrapper instance in this package. 76 77 func encodePkgWithStack( 78 _ context.Context, err error, 79 ) (msgPrefix string, safe []string, _ proto.Message) { 80 iErr := err.(interface{ StackTrace() pkgErr.StackTrace }) 81 safeDetails := []string{fmt.Sprintf("%+v", iErr.StackTrace())} 82 return "" /* withStack does not have a message prefix */, safeDetails, nil 83 } 84 85 func encodePathError( 86 _ context.Context, err error, 87 ) (msgPrefix string, safe []string, details proto.Message) { 88 p := err.(*os.PathError) 89 msg := p.Op + " " + p.Path 90 details = &errorspb.StringsPayload{ 91 Details: []string{p.Op, p.Path}, 92 } 93 return msg, []string{p.Op}, details 94 } 95 96 func decodePathError( 97 _ context.Context, cause error, _ string, _ []string, payload proto.Message, 98 ) (result error) { 99 m, ok := payload.(*errorspb.StringsPayload) 100 if !ok || len(m.Details) < 2 { 101 // If this ever happens, this means some version of the library 102 // (presumably future) changed the payload type, and we're 103 // receiving this here. In this case, give up and let 104 // DecodeError use the opaque type. 105 return nil 106 } 107 return &os.PathError{ 108 Op: m.Details[0], 109 Path: m.Details[1], 110 Err: cause, 111 } 112 } 113 114 func encodeLinkError( 115 _ context.Context, err error, 116 ) (msgPrefix string, safe []string, details proto.Message) { 117 p := err.(*os.LinkError) 118 msg := p.Op + " " + p.Old + " " + p.New 119 details = &errorspb.StringsPayload{ 120 Details: []string{p.Op, p.Old, p.New}, 121 } 122 return msg, []string{p.Op}, details 123 } 124 125 func decodeLinkError( 126 _ context.Context, cause error, _ string, _ []string, payload proto.Message, 127 ) (result error) { 128 m, ok := payload.(*errorspb.StringsPayload) 129 if !ok || len(m.Details) < 3 { 130 // If this ever happens, this means some version of the library 131 // (presumably future) changed the payload type, and we're 132 // receiving this here. In this case, give up and let 133 // DecodeError use the opaque type. 134 return nil 135 } 136 return &os.LinkError{ 137 Op: m.Details[0], 138 Old: m.Details[1], 139 New: m.Details[2], 140 Err: cause, 141 } 142 } 143 144 func encodeSyscallError( 145 _ context.Context, err error, 146 ) (msgPrefix string, safe []string, details proto.Message) { 147 p := err.(*os.SyscallError) 148 return p.Syscall, nil, nil 149 } 150 151 func decodeSyscallError( 152 _ context.Context, cause error, msg string, _ []string, _ proto.Message, 153 ) (result error) { 154 return os.NewSyscallError(msg, cause) 155 } 156 157 // OpaqueErrno represents a syscall.Errno error object that 158 // was constructed on a different OS/platform combination. 159 type OpaqueErrno struct { 160 msg string 161 details *errorspb.ErrnoPayload 162 } 163 164 // Error implements the error interface. 165 func (o *OpaqueErrno) Error() string { return o.msg } 166 167 // Is tests whether this opaque errno object represents a special os error type. 168 func (o *OpaqueErrno) Is(target error) bool { 169 return (target == os.ErrPermission && o.details.IsPermission) || 170 (target == os.ErrExist && o.details.IsExist) || 171 (target == os.ErrNotExist && o.details.IsNotExist) 172 } 173 174 // Temporary tests whether this opaque errno object encodes a temporary error. 175 func (o *OpaqueErrno) Temporary() bool { return o.details.IsTemporary } 176 177 // Timeout tests whether this opaque errno object encodes a timeout error. 178 func (o *OpaqueErrno) Timeout() bool { return o.details.IsTimeout } 179 180 func encodeOpaqueErrno( 181 _ context.Context, err error, 182 ) (msg string, safe []string, payload proto.Message) { 183 e := err.(*OpaqueErrno) 184 return e.Error(), []string{e.Error()}, e.details 185 } 186 187 func init() { 188 baseErr := goErr.New("") 189 RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString) 190 191 RegisterLeafDecoder(GetTypeKey(context.DeadlineExceeded), decodeDeadlineExceeded) 192 193 pkgE := pkgErr.New("") 194 RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental) 195 196 RegisterWrapperDecoder(GetTypeKey(pkgErr.WithMessage(baseErr, "")), decodeWithMessage) 197 198 ws := pkgErr.WithStack(baseErr) 199 RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack) 200 201 registerOsPathErrorMigration() // Needed for Go 1.16. 202 pKey := GetTypeKey(&os.PathError{}) 203 RegisterWrapperEncoder(pKey, encodePathError) 204 RegisterWrapperDecoder(pKey, decodePathError) 205 206 pKey = GetTypeKey(&os.LinkError{}) 207 RegisterWrapperEncoder(pKey, encodeLinkError) 208 RegisterWrapperDecoder(pKey, decodeLinkError) 209 pKey = GetTypeKey(&os.SyscallError{}) 210 RegisterWrapperEncoder(pKey, encodeSyscallError) 211 RegisterWrapperDecoder(pKey, decodeSyscallError) 212 213 RegisterLeafEncoder(GetTypeKey(&OpaqueErrno{}), encodeOpaqueErrno) 214 }