errors.go raw

   1  // Copyright (c) Microsoft Corporation.
   2  // Licensed under the MIT license.
   3  
   4  package errors
   5  
   6  import (
   7  	"errors"
   8  	"fmt"
   9  	"io"
  10  	"net/http"
  11  	"reflect"
  12  	"strings"
  13  
  14  	"github.com/kylelemons/godebug/pretty"
  15  )
  16  
  17  var prettyConf = &pretty.Config{
  18  	IncludeUnexported: false,
  19  	SkipZeroFields:    true,
  20  	TrackCycles:       true,
  21  	Formatter: map[reflect.Type]interface{}{
  22  		reflect.TypeOf((*io.Reader)(nil)).Elem(): func(r io.Reader) string {
  23  			b, err := io.ReadAll(r)
  24  			if err != nil {
  25  				return "could not read io.Reader content"
  26  			}
  27  			return string(b)
  28  		},
  29  	},
  30  }
  31  
  32  type verboser interface {
  33  	Verbose() string
  34  }
  35  
  36  // Verbose prints the most verbose error that the error message has.
  37  func Verbose(err error) string {
  38  	build := strings.Builder{}
  39  	for {
  40  		if err == nil {
  41  			break
  42  		}
  43  		if v, ok := err.(verboser); ok {
  44  			build.WriteString(v.Verbose())
  45  		} else {
  46  			build.WriteString(err.Error())
  47  		}
  48  		err = errors.Unwrap(err)
  49  	}
  50  	return build.String()
  51  }
  52  
  53  // New is equivalent to errors.New().
  54  func New(text string) error {
  55  	return errors.New(text)
  56  }
  57  
  58  // CallErr represents an HTTP call error. Has a Verbose() method that allows getting the
  59  // http.Request and Response objects. Implements error.
  60  type CallErr struct {
  61  	Req *http.Request
  62  	// Resp contains response body
  63  	Resp *http.Response
  64  	Err  error
  65  }
  66  
  67  type InvalidJsonErr struct {
  68  	Err error
  69  }
  70  
  71  // Errors implements error.Error().
  72  func (e CallErr) Error() string {
  73  	return e.Err.Error()
  74  }
  75  
  76  // Errors implements error.Error().
  77  func (e InvalidJsonErr) Error() string {
  78  	return e.Err.Error()
  79  }
  80  
  81  // Verbose prints a versbose error message with the request or response.
  82  func (e CallErr) Verbose() string {
  83  	e.Resp.Request = nil // This brings in a bunch of TLS crap we don't need
  84  	e.Resp.TLS = nil     // Same
  85  	return fmt.Sprintf("%s:\nRequest:\n%s\nResponse:\n%s", e.Err, prettyConf.Sprint(e.Req), prettyConf.Sprint(e.Resp))
  86  }
  87  
  88  // Is reports whether any error in errors chain matches target.
  89  func Is(err, target error) bool {
  90  	return errors.Is(err, target)
  91  }
  92  
  93  // As finds the first error in errors chain that matches target,
  94  // and if so, sets target to that error value and returns true.
  95  // Otherwise, it returns false.
  96  func As(err error, target interface{}) bool {
  97  	return errors.As(err, target)
  98  }
  99