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  }