github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/d/try.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     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 implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  // Package d implements several debug, error and assertion functions used throughout Noms.
    23  package d
    24  
    25  import (
    26  	"errors"
    27  	"fmt"
    28  	"reflect"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  )
    32  
    33  // d.Chk.<Method>() -- used in test cases and as assertions
    34  var (
    35  	Chk = assert.New(&panicker{})
    36  )
    37  
    38  type panicker struct {
    39  }
    40  
    41  func (s panicker) Errorf(format string, args ...interface{}) {
    42  	panic(fmt.Sprintf(format, args...))
    43  }
    44  
    45  // Panic(err) creates an error using format and args and wraps it in a
    46  // WrappedError which can be handled using Try() and TryCatch()
    47  func Panic(format string, args ...interface{}) {
    48  
    49  	if len(args) == 0 {
    50  		err := errors.New(format)
    51  		panic(Wrap(err))
    52  	}
    53  	err := fmt.Errorf(format, args...)
    54  	panic(Wrap(err))
    55  }
    56  
    57  // PanicIfError(err) && PanicIfTrue(expr) can be used to panic in a way that's
    58  // easily handled by Try() and TryCatch()
    59  func PanicIfError(err error) {
    60  	if err != nil {
    61  		panic(Wrap(err))
    62  	}
    63  }
    64  
    65  // If b is true, creates a default error, wraps it and panics.
    66  func PanicIfTrue(b bool) {
    67  	if b {
    68  		panic(Wrap(errors.New("expected true")))
    69  	}
    70  }
    71  
    72  // If b is false, creates a default error, wraps it and panics.
    73  func PanicIfFalse(b bool) {
    74  	if !b {
    75  		panic(Wrap(errors.New("expected false")))
    76  	}
    77  }
    78  
    79  type WrappedError interface {
    80  	Error() string
    81  	Cause() error
    82  }
    83  
    84  // Wraps an error. The enclosing error has a default Error() that contains the error msg along
    85  // with a backtrace. The original error can be retrieved by calling err.Cause().
    86  func Wrap(err error) WrappedError {
    87  	if err == nil {
    88  		return nil
    89  	}
    90  	if we, ok := err.(WrappedError); ok {
    91  		return we
    92  	}
    93  
    94  	st := stackTracer{}
    95  	assert := assert.New(&st)
    96  	assert.Fail(err.Error())
    97  
    98  	return wrappedError{st.stackTrace, err}
    99  }
   100  
   101  // If err is a WrappedError, then Cause() is returned, otherwise returns err.
   102  func Unwrap(err error) error {
   103  	cause := err
   104  	we, ok := err.(WrappedError)
   105  	if ok {
   106  		cause = we.Cause()
   107  	}
   108  	return cause
   109  }
   110  
   111  func causeInTypes(err error, types ...interface{}) bool {
   112  	cause := Unwrap(err)
   113  	typ := reflect.TypeOf(cause)
   114  	for _, t := range types {
   115  		if typ == reflect.TypeOf(t) {
   116  			return true
   117  		}
   118  	}
   119  	return false
   120  }
   121  
   122  // Utility method, that checks type of error and panics with wrapped error not one of the listed types.
   123  func PanicIfNotType(err error, types ...interface{}) error {
   124  	if err == nil {
   125  		return nil
   126  	}
   127  	if !causeInTypes(err, types...) {
   128  		we, ok := err.(WrappedError)
   129  		if !ok {
   130  			we = Wrap(err)
   131  		}
   132  		panic(we)
   133  	}
   134  	return Unwrap(err)
   135  }
   136  
   137  type wrappedError struct {
   138  	msg   string
   139  	cause error
   140  }
   141  
   142  func (we wrappedError) Error() string { return we.msg }
   143  func (we wrappedError) Cause() error  { return we.cause }
   144  
   145  type stackTracer struct {
   146  	stackTrace string
   147  }
   148  
   149  func (s *stackTracer) Errorf(format string, args ...interface{}) {
   150  	s.stackTrace = fmt.Sprintf(format, args...)
   151  }
   152  
   153  func recoverWrappedTypes(errp *error, types []interface{}) {
   154  	if r := recover(); r != nil {
   155  		if wrapper, ok := r.(wrappedError); !ok {
   156  			panic(r)
   157  		} else if len(types) > 0 && !causeInTypes(wrapper, types...) {
   158  			panic(r)
   159  		} else if len(types) > 0 {
   160  			*errp = wrapper.Cause()
   161  		} else {
   162  			*errp = wrapper
   163  		}
   164  	}
   165  }
   166  
   167  func recoverWrapped(errp *error, catch func(err error) error) {
   168  	if r := recover(); r != nil {
   169  		we, ok := r.(wrappedError)
   170  		if !ok {
   171  			panic(r)
   172  		}
   173  		if catch != nil {
   174  			*errp = catch(we)
   175  		} else {
   176  			*errp = Unwrap(we)
   177  		}
   178  	}
   179  }