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